Shadcn Button
Free shadcn/ui button component for React: solid, outline, ghost, link and destructive variants, plus icon and loading buttons. Copy or install in one command.
Installation
{
"registries": {
"@designrevision": "https://registry.designrevision.com/r/{name}.json"
}
}
Add to your existing components.json. Requires shadcn/ui v2.3+.
Packages
Props
variant
= default
'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
Visual style of the button.
size
= default
'default' | 'xs' | 'sm' | 'lg' | 'icon' | 'icon-sm' | 'icon-lg'
Size preset. The "icon" sizes render square icon-only buttons.
asChild
= false
boolean
Render as the child element via Radix Slot — e.g. to make a link look like a button.
Extends React.ButtonHTMLAttributes — all native <button> attributes are supported.
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
xs: "h-7 gap-1.5 rounded-md px-2.5 text-xs",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }
Examples
Simple
A versatile button with solid, ghost, outline and loading variants — keyboard-navigable and reduced-motion aware.
Packages
Props
No props documented yet.
"use client"
import { Button } from "@/components/ui/button"
export default function ButtonSimple01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button>Button</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
</div>
)
}
With icon
Drop a `lucide-react` icon before the label — the button auto-sizes icons and spaces them, so no extra classes are needed.
Packages
Props
No props documented yet.
import { Download, Plus, Send } from "lucide-react"
import { Button } from "@/components/ui/button"
export default function ButtonIcon01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button>
<Download />
Download
</Button>
<Button variant="secondary">
<Plus />
New project
</Button>
<Button variant="outline">
<Send />
Send message
</Button>
</div>
)
}
With trailing icon
Place the icon after the label for forward actions like “Continue” or “Open”. Same auto-sizing and spacing.
Packages
Props
No props documented yet.
import { ArrowRight, ChevronRight, ExternalLink } from "lucide-react"
import { Button } from "@/components/ui/button"
export default function ButtonIcon02() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button>
Continue
<ArrowRight />
</Button>
<Button variant="outline">
Open docs
<ExternalLink />
</Button>
<Button variant="ghost">
Next
<ChevronRight />
</Button>
</div>
)
}
Rounded
Add `rounded-full` for a pill shape. Works across every variant.
Packages
Props
No props documented yet.
import { Button } from "@/components/ui/button"
export default function ButtonRounded01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button className="rounded-full">Button</Button>
<Button variant="secondary" className="rounded-full">
Secondary
</Button>
<Button variant="outline" className="rounded-full">
Outline
</Button>
</div>
)
}
Loading
Show an in-flight action with a spinning `Loader2` icon and `disabled` to block repeat clicks.
Packages
Props
No props documented yet.
import { Loader2 } from "lucide-react"
import { Button } from "@/components/ui/button"
export default function ButtonLoading01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button disabled>
<Loader2 className="animate-spin" />
Saving
</Button>
<Button variant="secondary" disabled>
<Loader2 className="animate-spin" />
Please wait
</Button>
<Button variant="outline" disabled>
<Loader2 className="animate-spin" />
Loading
</Button>
</div>
)
}
With count
Pair the label with a `Badge` to surface a count — unread, pending, or selected items.
Packages
Props
No props documented yet.
import { Bell, Inbox, Mail } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
export default function ButtonCount01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button>
<Inbox />
Inbox
<Badge variant="secondary">24</Badge>
</Button>
<Button variant="secondary">
<Mail />
Messages
<Badge>5</Badge>
</Button>
<Button variant="outline">
<Bell />
Alerts
<Badge variant="destructive">9</Badge>
</Button>
</div>
)
}
With shortcut
Hint a keyboard shortcut with the `Kbd` component — common on search and command triggers.
Packages
Props
No props documented yet.
import { Search } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Kbd } from "@/components/ui/kbd"
export default function ButtonKbd01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button variant="outline">
<Search />
Search
<Kbd>⌘K</Kbd>
</Button>
<Button variant="secondary">
Save
<Kbd>⌘S</Kbd>
</Button>
<Button>
Command menu
<Kbd>⌘J</Kbd>
</Button>
</div>
)
}
Sizes
Four size presets — `xs`, `sm`, `default`, and `lg` — set via the `size` prop.
Packages
Props
No props documented yet.
import { Button } from "@/components/ui/button"
export default function ButtonSizes01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button size="xs">Extra small</Button>
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
</div>
)
}
Icon button
Square icon-only buttons with `size="icon"`. Always pass an `aria-label` for accessibility.
Packages
Props
No props documented yet.
import { Bell, Settings, Star } from "lucide-react"
import { Button } from "@/components/ui/button"
export default function ButtonIconOnly01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button size="icon" aria-label="Settings">
<Settings />
</Button>
<Button variant="secondary" size="icon" aria-label="Favorite">
<Star />
</Button>
<Button variant="outline" size="icon" aria-label="Notifications">
<Bell />
</Button>
</div>
)
}
Icon button sizes
Icon buttons at the `icon-sm`, `icon`, and `icon-lg` presets.
Packages
Props
No props documented yet.
import { Plus } from "lucide-react"
import { Button } from "@/components/ui/button"
export default function ButtonIconSizes01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button variant="outline" size="icon-sm" aria-label="Add">
<Plus />
</Button>
<Button variant="outline" size="icon" aria-label="Add">
<Plus />
</Button>
<Button variant="outline" size="icon-lg" aria-label="Add">
<Plus />
</Button>
</div>
)
}
Icon button with count
Overlay a `Badge` in the corner for a notification count.
Packages
Props
No props documented yet.
import { Bell } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
export default function ButtonIconCount01() {
return (
<div className="flex flex-wrap items-center gap-4">
<div className="relative inline-flex">
<Button variant="outline" size="icon" aria-label="Notifications">
<Bell />
</Button>
<Badge className="absolute -right-1.5 -top-1.5 size-5 justify-center rounded-full p-0 tabular-nums">
3
</Badge>
</div>
<div className="relative inline-flex">
<Button variant="outline" size="icon" aria-label="Notifications">
<Bell />
</Button>
<Badge
variant="destructive"
className="absolute -right-1.5 -top-1.5 size-5 justify-center rounded-full p-0 tabular-nums"
>
9
</Badge>
</div>
</div>
)
}
Icon button states
Rounded, loading, and disabled icon buttons.
Packages
Props
No props documented yet.
import { Heart, Loader2, Trash2 } from "lucide-react"
import { Button } from "@/components/ui/button"
export default function ButtonIconStates01() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button
variant="outline"
size="icon"
className="rounded-full"
aria-label="Like"
>
<Heart />
</Button>
<Button variant="outline" size="icon" disabled aria-label="Loading">
<Loader2 className="animate-spin" />
</Button>
<Button variant="outline" size="icon" disabled aria-label="Delete">
<Trash2 />
</Button>
</div>
)
}
The Shadcn Button is the foundation of every action in your interface — a drop-in shadcn/ui button built on Radix Slot and class-variance-authority, themed entirely with CSS variables. It ships six visual variants, seven size presets, ready-made icon and loading buttons, and an asChild escape hatch for rendering a link as a button.
When to use
Use a button for actions the user takes — submitting a form, opening a dialog, triggering a mutation. For navigation between pages, prefer a link (the link variant, or asChild wrapping an anchor) so the markup stays semantic and accessible.
Match the variant to emphasis: default for the single most important action in a view, secondary or outline for supporting actions, ghost for low-emphasis toolbar actions, and destructive for irreversible operations like deleting.
Button variants and sizes
Six variants cover the full emphasis scale: default, secondary, outline, ghost, link, and destructive. Seven sizes span xs through lg, plus three square icon sizes (icon-sm, icon, icon-lg) for icon-only buttons — set both with the variant and size props (see the Props panel in the Installation section for the full type signature). The button also auto-sizes any SVG child to 16px and spaces it from the label, so adding a lucide-react icon, a count Badge, or a Kbd shortcut hint needs no extra classes.
Accessibility
Every button is keyboard-navigable with a visible focus ring and respects prefers-reduced-motion. Icon-only buttons must carry an aria-label so screen readers announce the action. Disabled buttons set the disabled attribute (not just a class), so they are removed from the tab order and announced correctly.
Theming
The button reads your design tokens (--primary, --secondary, --ring, and friends), so it follows your theme — including dark mode — with zero overrides. Any palette produced by a shadcn-compatible theme generator applies automatically.