Button

Free · MIT

One button with ten variants, six tones, seven shadows, ten animations, three shapes and five sizes that all combine, plus loading and icon slots.

MarkdownOpen in v0
Open in v0

Installation

$ npx shadcn@latest add https://goodui.dev/r/button.json

The file lands in components/goodui/button.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/button works too.

Usage

import { ArrowRight } from "lucide-react"
import { Button } from "@/components/goodui/button"

export function Hero() {
  return (
    <div className="flex gap-3">
      <Button size="lg" shadow="glow" animation="arrow" iconRight={<ArrowRight />}>
        Start free trial
      </Button>
      <Button size="lg" variant="outline" tone="neutral">
        Book a demo
      </Button>
    </div>
  )
}

When to use

Use it for
  • Any action on a page: submit a form, open a dialog, start a checkout.
  • When one product needs several button styles that still match: solid for the primary action, soft or outline for secondary, ghost in toolbars.
  • Marketing pages that want motion (shine, shimmer, arrow) without a second library.
Reach for something else
  • Navigating to another page: use link (or render an <a> with the same classes), so the browser treats it as a link.
  • Turning a setting on and off: use switch.
  • Choosing one option from a few: use tabs or a segmented control.
If you needUse
The one main action on the screenvariant="solid" (add shadow="glow" for a hero CTA)
Secondary actions next to a primary onevariant="soft" or "outline"
Toolbars and dense tablesvariant="ghost" size="sm", iconOnly for single icons
Destructive actionstone="danger" with variant="soft"; solid only inside a confirm dialog
Playful or retro brandsvariant="neo" or "pushable"
On photos or colorful backgroundsvariant="glass"

Examples

Async action with loading

const [saving, setSaving] = React.useState(false)

<Button loading={saving} onClick={async () => { setSaving(true); await save(); setSaving(false) }}>
  Save changes
</Button>

Icon-only toolbar button

<Button variant="ghost" tone="neutral" size="sm" iconOnly aria-label="Notifications">
  <Bell />
</Button>

API reference

PropTypeDefault
variant"solid" | "soft" | "outline" | "ghost" | "link" | "gradient" | "glass" | "neo" | "pushable" | "dashed""solid"
tone"primary" | "neutral" | "success" | "warning" | "danger" | "info""primary"
shadow"none" | "sm" | "md" | "lg" | "glow" | "hard" | "inner""none"
animation"none" | "lift" | "press" | "shine" | "shimmer" | "ripple" | "pulse" | "arrow" | "glow" | "bounce""none"
shape"square" | "rounded" | "pill""rounded"
size"xs" | "sm" | "md" | "lg" | "xl""md"
iconOnly
Square button for a single icon.
boolean—
fullWidthboolean—
loading
Shows a spinner, keeps the width and disables the button.
boolean—
iconLeftReact.ReactNode—
iconRightReact.ReactNode—
sound
Plays "tap" on click when the visitor has sounds on (lib/goodui-sounds); pass a sound name to pick another. Off by default.
boolean | SoundName—

Also accepts every <button> attribute.

Accessibility

Enter / SpaceActivates the button (native <button>).
TabMoves focus; a focus ring shows on keyboard focus only.
  • Renders a native <button>, so roles, focus and form submission work without extra code.
  • loading sets disabled and aria-busy and keeps the width, so the layout does not jump.
  • iconOnly buttons need an aria-label; the icon itself is decorative.

Motion

  • Hover: lift rises 2px in 180 ms, glow blooms a tone-colored shadow, shine sweeps a highlight once, arrow nudges the icons.
  • Press: press scales to 0.95, bounce springs back from 0.92, ripple grows from the pointer.
  • Looping: shimmer sweeps every 2.6 s and pulse sends a ring every 2 s; keep looping buttons to one per screen.
  • Reduced motion: transitions, sweeps, rings and ripples are off; the loading spinner slows down instead of stopping.
  • All effects are CSS (transform, opacity, background-position) on the compositor; no JavaScript runs on hover. ripple adds one short-lived span per click.

Notes for AI

Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.

  • Layers are independent props: variant, tone, shadow, animation, shape, size. Combine them; do not write custom classes for colors.
  • Use tone for meaning (danger, success) instead of hard-coded colors like bg-red-500.
  • Pass icons through iconLeft and iconRight, not as children, so spacing and the arrow animation work.
  • For links use <a> or the link component; do not wrap Button in <a>.
  • Keep looping animations (shimmer, pulse) to one button per screen.

FAQ

Does it work with my shadcn theme?

Yes. Colors come from your theme tokens (--primary, --foreground…) through the tone variables, so it follows your theme and dark mode.

Can I use it as a link?

Use the link component for navigation. If you need the button look on a link, copy the classes onto an <a>.

Related