Button

Atomshadcn/ui, restyled with tokens

Starts an action. One primary per view; everything else is secondary or quieter.

components/atoms/button.tsx
Use it for
Submitting a form, confirming, opening a dialog — anything that does something on this page.
Not for
Going somewhere else. That is a Link, or a Button with asChild around one.

States

VariantsHover and press them
Sizes
Focused
Disabled
Loading
With a prefix icon
As a linkasChild around next/link

Usage

import { Button } from '@/components/atoms/button';

<Button variant="primary" type="submit" loading={isSaving}>
  Save & Continue
</Button>

// A link that looks like an action keeps its link semantics:
<Button asChild variant="soft">
  <Link href="/ui-showcase">See the components</Link>
</Button>

Props

Button props
PropTypeDefaultDescription
variant'primary' | 'outlined' | 'secondary' | 'soft' | 'ghost' | 'danger' | 'ghost-danger''secondary'How loud the action is. Danger is for actions that destroy something; ghost-danger is the quiet one, such as removing a row.
size'sm' | 'md' | 'lg' | 'icon''md'Padding and type size. Icon is a round button holding only an icon, and needs aria-label.
loadingbooleanfalseShows a spinner, sets aria-busy and blocks presses. The label stays.
asChildbooleanfalsePaints its single child (usually a Link) as a button instead of rendering a <button>.
prefixIconReactNode—An icon before the label, such as Google’s mark on “Continue with Google”. Decorative; the spinner replaces it while loading.
type'button' | 'submit' | 'reset''button'Defaults to button so it never submits a form by accident.
...restComponentProps<'button'>—Every native button prop, including ref and aria-*.

Accessibility

  • Reached with Tab, pressed with Enter or Space.
  • Named by its text. An icon-only button needs aria-label.
  • While loading it is announced as busy and keeps its name.