Components

Button

A button with variants, sizes and a built-in loading state. Never submits a form by accident.

Loading...

Installation

npx shadcn@latest add @fujin/button

Usage

import { Button } from "@/components/ui/button"
<Button variant="outline">Button</Button>

What's different from shadcn/ui

  • loading is built in. It shows a spinner, sets aria-busy, blocks clicks, keeps focus on the button and keeps its width so the layout doesn't jump.
  • type defaults to "button". A button inside a <form> no longer submits it by accident. Write type="submit" when you mean it.
  • The focus ring is solid. Alpha rings fall below the 3:1 contrast WCAG 1.4.11 requires.
  • render instead of asChild, from Base UI.

Examples

Loading

const [pending, startTransition] = React.useTransition()
 
<Button loading={pending} loadingText="Saving..." onClick={() => startTransition(save)}>
  Save
</Button>

Leave out loadingText to keep the label in place (invisible) with a spinner on top.

<Button render={<Link href="/billing" />} nativeButton={false}>
  Billing
</Button>

Icon only

Icon-only buttons need an aria-label.

<Button variant="ghost" size="icon" aria-label="Delete row">
  <TrashIcon />
</Button>

API Reference

Prop

Type

Every other prop is forwarded to the Base UI Button.

On this page