When to use
- Submitting forms
- Confirming decisions
- Opening dialogs
- Running page-level actions
Buttons trigger actions. Use them when the user is submitting, confirming, opening, or running something in the current workflow.
Start here before choosing examples or props. These notes explain where Button helps, where another pattern is better, and what should stay true in product work.
import { Button } from '@/components/ui/Button';Drop into product code; props mirror the native HTML button.
Choose the variant by action priority and risk. Visual emphasis should follow the workflow decision, not personal preference.
The main action in a decision area. Use once per local context.
An alternate action that should remain visible but lower emphasis.
A low-emphasis action used inside dense interfaces and secondary workflows.
A high-risk action that deletes, removes, or cannot be easily undone.
A button can be visually simple, but it still needs a stable structure so labels, icons, focus, disabled states, loading states, and motion do not shift the layout.
Every variant must respond the same way to user input. This matrix shows the visual feedback for each state on the primary variant; the contract applies to all variants.
At rest, awaiting input.
Pointer is over the control.
Reachable by keyboard.
Mid-press, before release.
Not currently available.
The component accepts every native HTMLButtonElement attribute on top of the design system props below.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'tertiary' | 'destructive' | 'primary' | Visual emphasis. Pick by action priority and risk, not preference. |
leadingIcon | ReactNode | — | Icon rendered before the label. Mark as decorative with aria-hidden. |
trailingIcon | ReactNode | — | Icon rendered after the label, e.g., a forward arrow for primary actions. |
disabled | boolean | false | Native HTML attribute. Suppresses hover and active styling; pair with helper text that explains why. |
type | 'button' | 'submit' | 'reset' | 'button' | Native HTML button type. Set submit explicitly inside forms. |
A page, form footer, dialog, or toolbar can have many actions, but only one should look like the next best step.
If the user is moving to another route, section, file, or external destination, use a link with clear destination text.
Write action-first labels like "Approve request", "Create vendor", or "Download report". Avoid vague labels like "OK".
If an action is disabled, the interface should explain what is missing or provide validation near the relevant fields.
What Button does not do yet, and what consumers need to compose themselves.
Agents should use this as the local contract when generating product UI, examples, documentation, or review comments.
import { Button } from '@/components/ui/Button';
import { ArrowRight } from 'lucide-react';
<Button
variant="primary"
type="submit"
trailingIcon={<ArrowRight size={16} strokeWidth={1.8} aria-hidden="true" />}
>
Save changes
</Button>Buttons should feel responsive without becoming distracting. Hover may lift the button, deepen its shadow, or nudge a trailing icon. Active state should press the button back toward the surface. Disable motion for users who request reduced motion.