Skip to main content
Components

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

Component Docs

FAQ