Button
Buttons for every kind of action, with variants by weight, sizes, a loading state and disabled tooltips.
Preview
Usage
import { Button } from '@vitnode/core/components/ui/button'<Button>
<SendIcon />
Post reply
</Button>A press dips the button to active:scale-97, so every click feels
acknowledged. Inside a Button Group it darkens
instead, and menu triggers stay put.
Variants
Pick the variant by the weight of the action, not your favourite colour:
default- the one primary action on the screen.secondary,outline,ghost- supporting actions, loudest to quietest.link- an action that behaves like navigation.success- approves or completes something.warning- risky but reversible, like unpublishing.destructive- deletes data for good.
Colour never carries the meaning alone. A green button still says "Approve".
Sizes
Icon-only sizes (icon, icon-xs, icon-sm, icon-lg) require an
aria-label, and TypeScript will remind you. On touch screens, icon,
icon-sm and icon-xs get an invisible 44px hit area.
Loading
isLoading swaps the content for a spinner and disables the button. Loading
always wins: disabled={false} won't re-enable it, and screen readers hear
"Loading" instead of your aria-label.
<Button isLoading={isPending}>Save changes</Button>Disabled with a reason
A greyed-out button with no explanation is a riddle. Add disabledTooltip to
say why.
<Button disabled disabledTooltip="You need the Publish permission">
Publish
</Button>The button then uses aria-disabled, so it stays focusable, clicks do nothing,
and screen readers read the reason as its description. The tooltip shows only
while disabled is true and hides during isLoading.
As a link
Use render to compose the button with another element. Set
nativeButton={false} when that element isn't a <button>.
import { Link } from '@tanstack/react-router'<Button nativeButton={false} render={<Link to="/" />}>
Home
</Button>Props
Prop
Type