Buttons allow users to take actions and make choices with a single tap. They communicate what action will occur when the user interacts with them.
Basics
The only required prop is children, which is the button's label. Buttons default to the primary variant.
<Button onClick={console.log}>Get started</Button>
Variants
Use variants to communicate the importance and intent of an action.
- Primary — High emphasis for main actions like "Save" or "Confirm". Limit to one per screen.
- Secondary — Medium emphasis for multiple actions of equal weight.
- Tertiary — High contrast with inverted background.
- Negative — Destructive actions that can't be undone. Use sparingly.
<HStack gap={2} flexWrap="wrap">
<Button onClick={console.log} variant="primary">
Primary
</Button>
<Button onClick={console.log} variant="secondary">
Secondary
</Button>
<Button onClick={console.log} variant="tertiary">
Tertiary
</Button>
<Button onClick={console.log} variant="negative">
Negative
</Button>
</HStack>
Transparent
Use transparent buttons for supplementary actions with lower prominence. The container is only visible on interaction. Works with any variant.
<HStack gap={2} flexWrap="wrap">
<Button onClick={console.log} transparent>
Primary
</Button>
<Button onClick={console.log} variant="secondary" transparent>
Secondary
</Button>
<Button onClick={console.log} variant="tertiary" transparent>
Tertiary
</Button>
<Button onClick={console.log} variant="negative" transparent>
Negative
</Button>
</HStack>
States
Loading
Use the loading prop to indicate an action is in progress. The button becomes non-interactive and displays a spinner while preserving its width.
function LoadingExample() {
const [isLoading, setIsLoading] = useState(false);
const handleClick = () => {
setIsLoading(true);
setTimeout(() => setIsLoading(false), 2000);
};
return (
<HStack gap={2} flexWrap="wrap">
<Button onClick={handleClick} loading={isLoading}>
Save changes
</Button>
<Button onClick={handleClick} variant="secondary" loading={isLoading}>
Submit
</Button>
</HStack>
);
}
Disabled
Use the disabled prop to prevent interaction and indicate the action is unavailable.
<HStack gap={2} flexWrap="wrap">
<Button disabled onClick={console.log}>
Primary
</Button>
<Button disabled onClick={console.log} variant="secondary">
Secondary
</Button>
<Button disabled onClick={console.log} variant="negative">
Negative
</Button>
</HStack>
Sizing
Compact
Use compact for smaller buttons with reduced padding. Useful in dense UIs or alongside other compact elements.
<HStack gap={2} flexWrap="wrap" alignItems="center">
<Button onClick={console.log} compact>
Compact
</Button>
<Button onClick={console.log}>Default</Button>
</HStack>
Block
Use block to make the button expand to fill its container width.
<VStack gap={2}>
<Button onClick={console.log} block>
Full width button
</Button>
<Button onClick={console.log} variant="secondary" block>
Another full width
</Button>
</VStack>
Icons
End Icon
Add an icon after the label to provide additional context or indicate direction.
<HStack gap={2} flexWrap="wrap">
<Button onClick={console.log} endIcon="forwardArrow" variant="secondary" compact>
See more
</Button>
<Button onClick={console.log} endIcon="externalLink" variant="secondary" compact>
Open link
</Button>
</HStack>
Start Icon
Add an icon before the label to reinforce the action.
<HStack gap={2} flexWrap="wrap">
<Button onClick={console.log} startIcon="add" startIconActive variant="secondary" compact>
Add item
</Button>
<Button onClick={console.log} startIcon="download" variant="secondary" compact>
Download
</Button>
</HStack>
Full Width with Icons
When using block with icons, the content automatically spreads across the button width.
<Button onClick={console.log} startIcon="wallet" endIcon="forwardArrow" variant="secondary" block>
Connect wallet
</Button>
Accessibility
Buttons automatically use their children as the accessible label. For buttons with only icons or ambiguous labels, provide an accessibilityLabel.
<Button onClick={handleClose} accessibilityLabel="Close dialog">
×
</Button>
Composed Examples
Confirmation Dialog
A common pattern using primary and secondary buttons together.
<HStack gap={2} flexWrap="wrap" justifyContent="flex-end">
<Button onClick={console.log} variant="secondary" transparent>
Cancel
</Button>
<Button onClick={console.log}>Confirm</Button>
</HStack>
Destructive Confirmation
Use negative buttons with a secondary cancel option for destructive actions.
<HStack gap={2} flexWrap="wrap" justifyContent="flex-end">
<Button onClick={console.log} variant="secondary" transparent>
Cancel
</Button>
<Button onClick={console.log} variant="negative">
Delete
</Button>
</HStack>
A full-width primary action with a compact secondary option.
<VStack gap={2}>
<Button onClick={console.log} block>
Create account
</Button>
<Button onClick={console.log} variant="secondary" transparent block>
Already have an account? Sign in
</Button>
</VStack>