import { useIcon } from "@/lib/icon-context";import { Button } from "@/components/ui/button";export default function ButtonDemo() {const Plus = useIcon("plus");const ArrowRight = useIcon("arrow-right");const Search = useIcon("search");return (<div className="flex flex-wrap items-center gap-2"><Button leadingIcon={Plus}>Create</Button><Button variant="secondary" trailingIcon={ArrowRight}>Next</Button><Button variant="tertiary" leadingIcon={Search} trailingIcon={ArrowRight}>Search</Button></div>);}
Installation
npx shadcn@latest add https://www.deltacomponents.dev/r/button.json
Usage
1import { Button } from "@/components/ui/button";23<Button>Button</Button>
Basic
Playground
import { Button } from "@/components/ui/button";<Button>Get started</Button>
Variants
Icon only
An icon-* size makes a square, one step for each text size, with icon-xl
the 48px step past shadcn's ladder. With no text the aria-label is the
accessible name, so it says what a press will do, not what the button shows.
import { useState } from "react";import { useIcon } from "@/lib/icon-context";import { Button } from "@/components/ui/button";type IconSize = "icon-sm" | "icon" | "icon-lg" | "icon-xl";export default function ButtonIcon() {const Play = useIcon("play");const Pause = useIcon("pause");const [playing, setPlaying] = useState(false);const [size, setSize] = useState<IconSize>("icon-xl");return (<div className="flex w-fit flex-col items-center gap-5">{/* No visible text, so the label is the accessible name, and it namesthe action the press will take, not the state the button shows. */}<Buttonsize={size}aria-label={playing ? "Pause" : "Play"}aria-pressed={playing}onClick={() => setPlaying((value) => !value)}>{/* A transport control is a filled shape: the glyph is the thingitself, not an outline of it. */}{playing ? <Pause className="fill-current" /> : <Play className="fill-current" />}</Button><label className="text-caption text-muted-foreground flex items-center gap-2 self-start">Size<selectvalue={size}onChange={(e) => setSize(e.target.value as IconSize)}className="border-border bg-background text-foreground text-caption rounded-md border px-2 py-1 outline-none focus-visible:ring-1 focus-visible:ring-[color:var(--focus-ring,#6B97FF)]"><option value="icon-sm">icon-sm</option><option value="icon">icon</option><option value="icon-lg">icon-lg</option><option value="icon-xl">icon-xl</option></select></label>
Loading & Disabled
import { useState } from "react";import { useIcon } from "@/lib/icon-context";import { Button } from "@/components/ui/button";export default function ButtonLoading() {const Loader = useIcon("loader");const [loading, setLoading] = useState(false);return (<div className="flex flex-wrap items-center gap-2"><Buttonloading={loading}onClick={() => {setLoading(true);setTimeout(() => setLoading(false), 2000);}}>{loading ? "Loading" : "Click me"}</Button><Button variant="secondary" loading leadingIcon={Loader}>Saving</Button><Button disabled>Disabled</Button></div>);}