Button
The primary action control.
Button is the single control for both actions and button-styled navigation.
Given an href it renders a real anchor (<a>) — keeping middle-click,
copy-link, and open-in-new-tab — and without one it renders a <button>.
Either way the styling, variants, and states are identical.
import { Button } from '@base-org/bds-web/product/button';
<Button variant="primary" size="md">
Continue
</Button>;Variants
Primary
<Button variant="primary">Primary</Button>Secondary
<Button variant="secondary">Secondary</Button>Outline
<Button variant="outline">Outline</Button>Ghost
<Button variant="ghost">Ghost</Button>Negative
<Button variant="negative">Negative</Button>Outline Negative
<Button variant="outline-negative">Outline Negative</Button>Bare
<Button variant="bare">Bare</Button>| Variant | Use for |
|---|---|
primary | The single most important action in a view. |
secondary | Supporting actions alongside a primary. |
outline | Actions on a busy or coloured background. |
ghost | Low-emphasis actions inside dense UI. |
negative | Destructive actions, paired with a confirmation. |
outline-negative | A destructive action that should not outweigh the primary action beside it. |
bare | Text-only actions with no container or fixed height, such as a banner action or a close icon. |
Sizes
Small is 32px tall with a 12px label, medium 36px with a 14px label, and large 48px with a 16px label.
Small
<Button size="sm">Small</Button>Medium
<Button size="md">Medium</Button>Large
<Button size="lg">Large</Button>md is the default and the right choice inside forms and toolbars. Use lg
for a standalone call to action, such as a section call to action on
base.org, and sm for dense tables, inline controls and card footers.
Every button in a base.org hero is lg, including the secondary action
beside the primary one, so the pair reads as one row.
Icons
With icons
<Button variant="secondary" startIcon={<PlusIcon />}>
New Project
</Button>
<Button variant="secondary" endIcon={<ArrowRightIcon />}>
Continue
</Button>Icon Only
<Button variant="outline" startIcon={<PlusIcon />} aria-label="Add" />startIcon and endIcon are sized to match the button. A button with only a
startIcon and no children becomes a square icon-only button and needs an
aria-label.
Arrows
Arrow
arrow appends an animated chevron in place of endIcon. On hover the shaft
draws in and the arrow slides right; under reduced motion it is static. The
chevron sits 4px closer to the edge than the label does on the other side.
<Button size="lg" arrow>Start Building</Button>
<Button size="lg" variant="secondary" arrow>Learn More</Button>External Arrow
externalArrow appends a 9px ↗ for links that leave the site. With an href
and no target, the link opens in a new tab with target="_blank" and
rel="noopener noreferrer"; set target yourself to keep it in the same tab.
Use it instead of arrow, not with it.
<Button href="https://docs.base.org" variant="outline" externalArrow>
Read The Docs
</Button>Status
<Button status="pending">Saving</Button>
<Button status="success" successIcon={<CheckIcon />}>Saved</Button>status moves an async action through idle → pending → success.
pending shows the circle
Spinner in the start slot and blocks presses. success
hides the label and end icon and shows successIcon — there is no default
glyph, so always pass one. Return to idle yourself once the moment has
passed.
As a link
Pass href and Button renders an anchor with the same look and states. Use
this for navigation. A link still shows the pending spinner but does not block
presses, so keep status for buttons that run an action. LinkButton is
deprecated in favour of this — see Link Button
to migrate.
<Button href="/get-started" variant="primary">
Get Started
</Button>Secondary link
<Button href="#" variant="secondary">
Learn More
</Button>For an inline text link inside a sentence, use Link instead.
When to use
Use Button for any standalone action, and for button-styled navigation by
passing href.
Use something else when:
- The link sits inside a sentence. Use Link.
- Several secondary actions on one object would crowd the surface. Collect them in a Menu.
- The reader is choosing a value, not firing an action. Use Segment or Select.
- It turns a setting on or off. Use Switch.
Usage
Do
Keep one primary button per view. If two actions look equally important,
the view has not decided what it is for.
Don't
Use negative for anything reversible. It is reserved for actions that
destroy data or cannot be undone.
- Use
arrowon the action that moves the reader forward, and leave it off the secondary action beside it. - Reserve
externalArrowfor destinations outside the site, so the ↗ keeps its meaning. - Content is centered by default. A
justify-*class overrides it, for examplejustify-betweenon a full-width or Select trigger to start-align the label and push the end icon to the edge.
Props
| Prop | Type | Default |
|---|---|---|
arrowAppends an animated chevron in place of `endIcon`. Its shaft draws in and | Typeboolean | Defaultfalse |
childrenThe children of the component. A function may be provided to alter the children based on | Typestring | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 5 more ... | Default— |
classNameThe CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the | TypeClassNameOrFunction<ButtonRenderProps> | Default— |
endIcon | TypeReactNode | ((renderProps: ButtonRenderProps) => ReactNode) | ((renderProps: LinkRenderProps) => ReactNode) | Default— |
externalArrowAppends a small ↗ in place of `endIcon` for links that leave the site, and | Typeboolean | Defaultfalse |
hrefA URL to link to. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#href). | Typestring | Default— |
isDisabledWhether the button is disabled. | Typeboolean | Default— |
relThe relationship between the linked resource and the current page. See | Typestring | Default— |
size | Type"sm" | "md" | "lg" | null | Defaultmd |
startIcon | TypeReactNode | ((renderProps: ButtonRenderProps) => ReactNode) | ((renderProps: LinkRenderProps) => ReactNode) | Default— |
status | Type"idle" | "pending" | "success" | Defaultidle |
successIcon | TypeReactNode | Default— |
targetThe target window for the link. See | TypeHTMLAttributeAnchorTarget | Default— |
title | Typestring | Default— |
typeThe behavior of the button when used in an HTML form. | Type"button" | "submit" | "reset" | Default'button' |
variant | Type"outline" | "outline-negative" | "secondary" | "primary" | "negative" | "ghost" | "bare" | null | Defaultprimary |
Accessibility
Button wraps React Aria's Button, so keyboard activation, focus management,
and disabled semantics come from the primitive. Icon-only buttons must carry an
accessible name via aria-label or aria-labelledby — in development, an
icon-only button shipped without one logs a console warning.
Pressing scales the button down slightly, and colour changes skip their
transition for keyboard focus. When the reader prefers reduced motion, the
press scale, the icon/label crossfade, and the colour transitions all
collapse to instant changes. A
status change — idle → pending → success — is announced to screen
readers through a visually-hidden role="status" region, not just the icon
swap.
The arrow and externalArrow icons are decorative SVGs hidden from
assistive technology, so the label alone must say where the button goes. The
↗ tells sighted readers a link opens a new tab; say so in the label when it
matters.