Button
A button, or a link that looks like one, in six variants and eight sizes.
Use a button for an action on the current page: save, open a dialog, add a row. Use a link for navigation to another page; when it must look like a button, give the link the button's classes (below), not the button a link's role.
// apps/docs/examples/ui/button-variants.tsx'use client';
import {Button} from '@workspace/active-ui/components/primitives/button';
/** The six variants: one primary action, then decreasing emphasis. */export default function ButtonVariants() { return ( <div className="flex flex-wrap items-center gap-2"> <Button>Default</Button> <Button variant="secondary">Secondary</Button> <Button variant="outline">Outline</Button> <Button variant="ghost">Ghost</Button> <Button variant="destructive">Destructive</Button> <Button variant="link">Link</Button> </div> );}Usage#
import {Button} from '@workspace/active-ui/components/primitives/button';
<Button variant="outline">Export</Button>;Variants#
variant sets the emphasis. Use one default button for the main action of a view, and lower
emphasis for the rest:
default: the main action;secondary,outline: other actions;ghost: actions in toolbars and lists, where a border would add noise;destructive: an action that deletes or cannot be undone;link: an action that reads like text.
Sizes and icons#
size sets the height and padding. The icon sizes make a square button for a single icon.
// apps/docs/examples/ui/button-sizes.tsx'use client';
import {ArrowRightIcon, PlusIcon} from 'lucide-react';
import {Button} from '@workspace/active-ui/components/primitives/button';
/** * Sizes, icons beside text, and icon-only buttons. An icon-only button has * no visible text, so it needs an accessible name of its own. */export default function ButtonSizes() { return ( <div className="flex flex-col gap-4"> <div className="flex flex-wrap items-center gap-2"> <Button size="xs">Extra small</Button> <Button size="sm">Small</Button> <Button>Default</Button> <Button size="lg">Large</Button> </div> <div className="flex flex-wrap items-center gap-2"> <Button variant="outline"> <PlusIcon data-icon="inline-start" aria-hidden /> New scheme </Button> <Button variant="outline"> Continue <ArrowRightIcon data-icon="inline-end" aria-hidden /> </Button> <Button variant="outline" size="icon" aria-label="Add a row"> <PlusIcon aria-hidden /> </Button> <Button variant="outline" size="icon-sm" aria-label="Add a column"> <PlusIcon aria-hidden /> </Button> </div> </div> );}Mark an icon beside text with data- or data-: the button
then tightens its padding on that side. An icon-only button has no visible text, so it needs an
accessible name of its own, such as aria-.
States and links#
// apps/docs/examples/ui/button-states.tsx'use client';
import {useState} from 'react';
import { Button, buttonVariants,} from '@workspace/active-ui/components/primitives/button';import {Spinner} from '@workspace/active-ui/components/primitives/spinner';
/** * Disabled, busy, and a link that looks like a button. While busy, the * button is disabled, so a second click cannot start the action twice. */export default function ButtonStates() { const [busy, setBusy] = useState(false); const save = () => { setBusy(true); window.setTimeout(() => setBusy(false), 1500); }; return ( <div className="flex flex-wrap items-center gap-2"> <Button disabled>Disabled</Button> <Button onClick={save} disabled={busy} data-busy={busy || undefined}> {busy ? <Spinner data-icon="inline-start" /> : null} {busy ? 'Saving…' : 'Save'} </Button> <a href="#api" className={buttonVariants({variant: 'outline'})}> A link, styled as a button </a> </div> );}disabledremoves the button from the tab order and from pointer input. Disable a button while its action runs, so a second click cannot start it twice, and show aSpinnerbeside the label.For a link, apply
buttonto the link element itself. The Base UI button always carriesVariants() role="button", so rendering it as an<a>would hide the link from assistive technology.
What your application provides#
The label, and an accessible name for an icon-only button.
The busy state: the button shows what you pass; it does not track a pending action.
Spinneris decorative unless you give it alabel; inside a button whose text already says "Saving…", leave it decorative.
Accessibility and keyboard#
A native
<button>:EnterandSpaceactivate it,Tabreaches it.Inside a form, a
Buttondoes not submit: it renderstype="button"unless you pass another type. Give the button that sends the formtype="submit".The focus ring appears on keyboard focus only.
API#
Button#
Import from @workspace/
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
Functions, hooks and constants
button returns the class names of a button, for an element that is not
a button.
This page adapts the Button documentation of shadcn/ui (MIT licence) to this package; see Third-party notices.