Button

A button is the thing a person clicks to make something happen. button() renders a real <button> with type="button", so dropping one inside a form does not submit it by accident.

Basic button

The default is a solid, primary, medium button. Children are the label; anything else is a prop.

stack({ direction: 'row', gap: 'md' },
  button('One'),
  button('Two'),
  button('Three'),
)

Variants

Five levels of emphasis, from the one call to action on the page down to something that reads as a link.

stack({ direction: 'row', gap: 'md', wrap: true },
  button({ variant: 'solid' }, 'Solid'),
  button({ variant: 'soft' }, 'Soft'),
  button({ variant: 'outline' }, 'Outline'),
  button({ variant: 'ghost' }, 'Ghost'),
  button({ variant: 'link' }, 'Link'),
)

Colors

Every themed component draws from the same five palettes, so a color: 'danger' button and a color: 'danger' alert agree without either of them knowing about the other.

stack({ direction: 'row', gap: 'md', wrap: true },
  button({ color: 'primary' }, 'Primary'),
  button({ color: 'neutral' }, 'Neutral'),
  button({ color: 'success' }, 'Success'),
  button({ color: 'warning' }, 'Warning'),
  button({ color: 'danger' }, 'Danger'),
)
stack({ direction: 'row', gap: 'md', wrap: true },
  button({ variant: 'soft', color: 'primary' }, 'Primary'),
  button({ variant: 'soft', color: 'neutral' }, 'Neutral'),
  button({ variant: 'soft', color: 'success' }, 'Success'),
  button({ variant: 'soft', color: 'warning' }, 'Warning'),
  button({ variant: 'soft', color: 'danger' }, 'Danger'),
)

Sizes

stack({ direction: 'row', gap: 'md', align: 'center', wrap: true },
  button({ size: 'sm' }, 'Small'),
  button({ size: 'md' }, 'Medium'),
  button({ size: 'lg' }, 'Large'),
)

Icons

Pass any SVG markup as startIcon or endIcon. Icons are sized in em, so they scale with the button rather than needing a size of their own.

stack({ direction: 'row', gap: 'md', wrap: true },
  button({
    startIcon: icon('plus'),
  }, 'New page'),
  button({
    variant: 'outline',
    endIcon: icon('arrow-right'),
  }, 'Continue'),
)

Icon button

iconButton() is a square button holding a single icon. Its label is required — it becomes the accessible name that the icon itself cannot provide, and the tooltip on hover.

stack({ direction: 'row', gap: 'md' },
  iconButton({ label: 'Add', icon: icon('plus') }),
  iconButton({ label: 'Edit', variant: 'soft', icon: icon('edit') }),
  iconButton({ label: 'Delete', variant: 'ghost', color: 'danger', icon: icon('trash') }),
)

Loading and disabled

loading swaps in a spinner and sets aria-busy, leaving the label in place so the button does not change width mid-action.

stack({ direction: 'row', gap: 'md', wrap: true },
  button({ loading: true }, 'Saving'),
  button({ variant: 'outline', loading: true }, 'Checking'),
  button({ disabled: true }, 'Disabled'),
  button({ variant: 'soft', disabled: true }, 'Disabled'),
)

Give a button an href and it renders an <a> instead — the right element for something that navigates. Anchors have no disabled state, so disabled becomes aria-disabled and removes it from the tab order.

stack({ direction: 'row', gap: 'md', wrap: true },
  button({ href: '/docs' }, 'Read the docs'),
  button({ href: '/docs/ui', variant: 'outline' }, 'Components'),
  button({ href: '/docs', variant: 'link' }, 'Plain link'),
)

Full width

button({ block: true, size: 'lg' }, 'Deploy site')

Props

Anything not listed here falls through to the rendered element as an attribute — id, data-*, onclick, popovertarget and the rest.

PropTypeDefaultDescription
variant'solid' | 'soft' | 'outline' | 'ghost' | 'link''solid'How much emphasis the button carries.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''primary'Which palette to draw from.
size'sm' | 'md' | 'lg''md'Height, padding and label size.
hrefstringRenders an <a> instead of a <button>.
startIconstringMarkup placed before the label.
endIconstringMarkup placed after the label.
loadingbooleanfalseShows a spinner and sets aria-busy.
disabledbooleanfalseDisables the button, or marks a link aria-disabled.
blockbooleanfalseStretches to the full width of its container.
type'button' | 'submit' | 'reset''button'Only for the <button> form.

iconButton() takes the same props plus a required label and an icon.