Button
Four types (primary / outline / ghost / link), four sizes (sm / md / lg / xl), a destructive tone and an iconOnly mode — mirroring the Figma variant properties 1:1. Hover and press any example to see its interactive states.
Variants
variant controls the visual type. Default is primary.
<Button>Button</Button>
<Button variant="outline">Button</Button>
<Button variant="ghost">Button</Button>
<Button variant="link">Button</Button>Sizes
sm 24px / md 32px (default) / lg 40px / xl 48px tall. Link ignores box sizing and scales its text only.
<Button size="sm">Button</Button>
<Button size="md">Button</Button>
<Button size="lg">Button</Button>
<Button size="xl">Button</Button>Destructive
destructive switches every variant to the error palette without changing its shape.
<Button destructive>Delete</Button>
<Button destructive variant="outline">Delete</Button>
<Button destructive variant="ghost">Delete</Button>
<Button destructive variant="link">Delete</Button>Disabled
Native disabled attribute; every variant collapses to the grey disabled palette.
<Button disabled>Button</Button>
<Button disabled variant="outline">Button</Button>
<Button disabled destructive>Delete</Button>With icons
Any svg child is auto-sized per button size (16px on sm/md, 20px on lg/xl).
<Button><Plus /> New project</Button>
<Button variant="outline">Export <ChevronDown /></Button>
<Button variant="ghost"><Search /> Search</Button>
<Button variant="link">Learn more <ArrowRight /></Button>
<Button destructive><Trash2 /> Delete</Button>Icon only
iconOnly makes the button square (24/32/40/48px). The link variant has no icon-only form in the design. Always provide aria-label.
<Button iconOnly aria-label="Settings"><Settings /></Button>
<Button iconOnly variant="outline" size="lg" aria-label="Add"><Plus /></Button>
<Button iconOnly destructive variant="ghost" aria-label="Delete"><Trash2 /></Button>As a link (asChild)
asChild merges button styling onto the child element — use it to style anchors or Next.js <Link>.
<Button asChild>
<Link href="/components/button">Open docs</Link>
</Button>
<Button asChild variant="link">
<a href="https://example.com" target="_blank" rel="noreferrer">
External link <ArrowRight />
</a>
</Button>Loading
loading overlays a centered spinner without shifting width and blocks re-triggering (aria-busy). It is not the disabled look — the user is waiting on their own action.
<Button loading={pending} onClick={submit}>Recalculate score</Button>
<Button variant="outline" loading>Saving</Button>Composition
className merges via tailwind-merge; buttonVariants() exposes the class generator for non-React targets.
<Button className="w-full" size="lg">Continue</Button>
import { buttonVariants } from '@kothry/ui';
<a className={buttonVariants({ variant: 'outline', size: 'sm' })}>Anchor</a>