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;
}