Overview
When to use
- Primary call-to-action in hero sections, dialogs, and cards.
- Secondary, destructive, and ghost styles for contextual actions.
- Use the Slot-powered `asChild` prop to style Next.js `Link` or custom components.
Avoid when
- Use `Link` without `asChild` for in-flow navigation that isn’t an action.
- Toggle states are better handled with `Toggle` or `Switch` primitives.
- Inline text actions inside copy should use the `link` variant sparingly.
Variants
Semantic variants
Each variant maps to a semantic color token so buttons stay accessible in light and dark modes.
import { Button } from "@/components/ui/button"
export function VariantExamples() {
return (
<div className="flex flex-wrap gap-3">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="subtle">Subtle</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Danger</Button>
<Button variant="link">Link style</Button>
</div>
)
}Size presets
Sizes align with the 4px spacing scale. `icon` keeps square proportions for icon-only buttons.
import { Button } from "@/components/ui/button"
export function SizeExamples() {
return (
<div className="flex items-center gap-3">
<Button size="xs">XS</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button size="xl">XL</Button>
<Button size="icon" aria-label="Settings">
<IconSettings className="h-4 w-4" />
</Button>
</div>
)
}Slot rendering
The `asChild` prop lets you render the button as any element while keeping focus styles and animations.
import Link from "next/link"
import { Button } from "@/components/ui/button"
export function ButtonLink() {
return (
<Button asChild>
<Link href="/projects/new">Create project</Link>
</Button>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link' | 'default' | Visual style that maps to Pixexid semantic tokens. |
size | 'sm' | 'default' | 'lg' | 'icon' | 'default' | Controls height and horizontal padding. |
asChild | boolean | false | When true, renders the Slot component so you can pass any element (e.g., Link) while preserving button styles. |
...rest | React.ButtonHTMLAttributes<HTMLButtonElement> | — | All native button props like type, disabled, aria-* are forwarded to the underlying element. |
Usage examples
Primary form submit
<form className="space-y-4">
<input className="w-full rounded-md border border-input bg-background px-3 py-2" placeholder="Project name" />
<Button type="submit">Create project</Button>
</form>Button group with icon action
<div className="flex gap-2">
<Button variant="secondary">Save draft</Button>
<Button>Publish</Button>
<Button size="icon" variant="outline" aria-label="Settings">
<IconSettings className="h-4 w-4" />
</Button>
</div>Accessibility
Keyboard
- Tab moves focus between buttons in DOM order.
- Enter or Space triggers `onClick`.
- Provide `aria-label` or visible text for icon-only buttons.
Best practices
- Use one `variant="default"` per view to keep focus on the primary action.
- Ensure destructive actions require a confirmation dialog.
- Combine `disabled` state with explanatory messaging when an action is unavailable.