Button
Triggers an action, such as saving a form or opening a dialog. For navigation, use Link, or ButtonLink for a link that looks like a button.
Code
import {Button} from '@earendil-works/elanor-react';
export default function ButtonDemo() {
return <Button>Save changes</Button>;
} Usage
import {Button} from '@earendil-works/elanor-react';
<Button onClick={save}>Save changes</Button>;
Button renders type="button" by default. Set type="submit" to submit a form.
Hierarchy
Use one primary button per view for the main action. Use secondary and tertiary for supporting actions.
Code
import {Button, Stack} from '@earendil-works/elanor-react';
export default function ButtonHierarchy() {
return (
<Stack direction="row" gap="small" wrap="wrap">
<Button>Primary</Button>
<Button hierarchy="secondary">Secondary</Button>
<Button hierarchy="tertiary">Tertiary</Button>
</Stack>
);
} Semantic
negative marks destructive actions; positive marks confirming ones. Combine with any hierarchy.
Code
import {Button, Stack} from '@earendil-works/elanor-react';
export default function ButtonSemantic() {
return (
<Stack direction="row" gap="small" wrap="wrap">
<Button semantic="negative">Delete project</Button>
<Button semantic="positive">Approve request</Button>
</Stack>
);
} Density
Use compact in dense layouts, such as toolbars and table rows. The touch target keeps the default height.
Code
import {Button, Stack} from '@earendil-works/elanor-react';
export default function ButtonDensity() {
return (
<Stack direction="row" gap="small" align="center" wrap="wrap">
<Button>Default</Button>
<Button density="compact">Compact</Button>
</Stack>
);
} Icons
iconStart and iconEnd add decorative icons; the label names the button. For an icon without a label, use Icon button.
Code
import {AddIcon, ArrowForwardIcon} from '@earendil-works/elanor-icons-react';
import {Button, Stack} from '@earendil-works/elanor-react';
export default function ButtonIcons() {
return (
<Stack direction="row" gap="small" wrap="wrap">
<Button iconStart={<AddIcon />}>New project</Button>
<Button hierarchy="secondary" iconEnd={<ArrowForwardIcon />}>
Continue
</Button>
</Stack>
);
} Pending
pending disables the button while an action runs, keeps it focusable, and sets aria-busy.
Code
import {Button} from '@earendil-works/elanor-react';
import {useState} from 'react';
export default function ButtonPending() {
const [pending, setPending] = useState(false);
return (
<Button
pending={pending}
onClick={() => {
setPending(true);
setTimeout(() => setPending(false), 2000);
}}
>
{pending ? 'Saving…' : 'Save changes'}
</Button>
);
} Links
ButtonLink renders a plain a that looks like a Button, for navigation such as a call to action. It takes the same hierarchy, semantic, density, iconStart, and iconEnd, and keeps link semantics: screen readers announce it as a link, and Enter follows it. Don’t pass a link to Button’s render; that gives the a a button role.
Code
import {ArrowForwardIcon} from '@earendil-works/elanor-icons-react';
import {ButtonLink, Stack} from '@earendil-works/elanor-react';
export default function ButtonLinkDemo() {
return (
<Stack direction="row" gap="small" wrap="wrap">
<ButtonLink href="#" iconEnd={<ArrowForwardIcon />}>
Open dashboard
</ButtonLink>
<ButtonLink href="#" hierarchy="secondary">
Read the guide
</ButtonLink>
</Stack>
);
} ButtonLink passes its props and ref through to the a, so a router can wrap it, as with TanStack Router’s createLink.
A link can’t be disabled or pending. When a destination isn’t available, render a Button or text instead.
Disabled
Code
import {Button} from '@earendil-works/elanor-react';
export default function ButtonDisabled() {
return <Button disabled>Save changes</Button>;
} API
Button
| Prop | Type | Default | Description |
|---|---|---|---|
hierarchy | 'primary' | 'secondary' | 'tertiary' | 'primary' | Visual emphasis. Use one primary button per view. |
semantic | 'neutral' | 'negative' | 'positive' | 'neutral' | negative for destructive actions, positive for confirming ones. |
density | 'compact' | 'default' | 'default' | compact removes block padding but keeps the touch target height. |
iconStart | ReactNode | - | Decorative icon before the label. |
iconEnd | ReactNode | - | Decorative icon after the label. |
pending | boolean | false | Disables the button while an action runs, keeps it focusable, and sets aria-busy. |
type | 'submit' | 'reset' | 'button' | 'button' |
Also accepts Base UI Button props.
ButtonLink
A link that looks like a Button, for navigation such as a call to action. Renders a plain a without a button role, and passes its props and ref through, so a router can wrap it.
| Prop | Type | Default | Description |
|---|---|---|---|
hierarchy | 'primary' | 'secondary' | 'tertiary' | 'primary' | Visual emphasis. Use one primary button per view. |
semantic | 'neutral' | 'negative' | 'positive' | 'neutral' | negative for destructive actions, positive for confirming ones. |
density | 'compact' | 'default' | 'default' | compact removes block padding but keeps the touch target height. |
iconStart | ReactNode | - | Decorative icon before the label. |
iconEnd | ReactNode | - | Decorative icon after the label. |
Renders <a>. Also accepts its HTML attributes and ref.