Actions
Button
The pill button for every action. Six variants, four text sizes and four icon sizes, on Base UI's Button so it can render as a link.
packages/design-system/components/ui/button.tsx
Examples
Anatomy
- 01Pill, rounded-4xl, transparent border
- 02Optional leading icon, data-icon="inline-start"
- 03Label, text-sm medium
- 04Optional trailing icon, data-icon="inline-end"
Usage
import { Button } from "@repo/design-system/components/ui/button";
import Link from "next/link";
<Button>Start a project</Button>
<Button variant="outline" size="sm">See work</Button>
<Button size="icon" aria-label="Add"><PlusIcon /></Button>
// A link that looks like a button
<Button nativeButton={false} render={<Link href="/contact" />}>
Contact
</Button>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | "default" | Default is the primary fill. Destructive is a tinted red, not a solid one. |
| size | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | "default" | Heights 24, 32, 36 and 40px. The icon sizes are square. |
| render | ReactElement | (props, state) => ReactElement | none | Base UI's replacement for asChild: render the part as another element, such as a Button or a Link. |
| nativeButton | boolean | true | Set false when render is not a <button>, so Base UI adds the right role and keys. |
| disabled | boolean | false | Half opacity, no pointer events. |
| className | string | none | Merged last with cn(). Use it for layout (width, margin), not to restyle the component. |
Guidance
Do
- Use one default button per view for the main action; outline or ghost for the rest.
- Give icon-only buttons an aria-label.
Don't
- Wrap a Button in a Link. Use render with nativeButton={false}.
- Recolour it with className. If a variant is missing, it belongs in the cva.