React Button Components
Accessible, customizable button components for React with Tailwind CSS v4. Variants, sizes, loading states, icons — all with Radix-powered accessibility.
import { Button } from "@ninna-ui/primitives";
<Button color="primary" size="md">Click me</Button><Button variant="outline" size="sm">Outline</Button><Button variant="ghost" isLoading>Saving…</Button>Key features
6 variants: solid, outline, ghost, soft, link, plain
4 sizes: sm, md, lg, xl with consistent spacing
Loading state with spinner — no extra component needed
Icon support via startIcon / endElement props
Full keyboard navigation and ARIA compliance
data-slot CSS targets for deep customization
Why Ninna UI buttons
Buttons are the most-used component in any React app. Ninna UI's Button ships with six variants, four sizes, loading states, and icon support — all accessible by default via Radix UI internals.
Every button variant is styled with Tailwind CSS v4 utilities and CSS custom properties, so you can re-theme the entire button system by changing a few oklch color tokens. No JavaScript provider, no runtime cost.
Variants and sizes
The variant prop controls the visual style, while size controls padding and font size. Both are typed — you get autocomplete in VS Code and a compile-time error if you pass an invalid value.
<Button color="primary">Solid (default)</Button><Button variant="outline" color="primary">Outline</Button><Button variant="ghost">Ghost</Button><Button variant="soft" color="success">Soft</Button><Button variant="link">Link</Button>
<Button size="sm">Small</Button><Button size="md">Medium</Button><Button size="lg">Large</Button>Loading state
Pass isLoading to show a spinner and disable the button. The label stays visible so the button doesn't shift layout — important for Core Web Vitals (CLS).
function SaveButton() { const [saving, setSaving] = useState(false); return ( <Button color="primary" isLoading={saving} onClick={async () => { setSaving(true); await save(); setSaving(false); }} > Save changes </Button> );}Customizing with data-slot
Need a pill-shaped button? Target the data-slot attribute instead of overriding classes:
[data-slot="button"] { border-radius: 9999px;}