Shadcn Button
The shadcn/ui Button is a copy-paste React component for actions and links, styled with Tailwind CSS v4 and built on Base UI. It ships six variants and four sizes, supports icons and loading states, and can render as a link. It is the foundation for buttons in every ShadcnStore block.
Key features
- Six variants (default, secondary, destructive, outline, ghost, link) and four sizes from single props.
- Renders as any element via the
renderprop, so it works as a link or custom trigger. - Built-in spacing for icons and a simple disabled-plus-spinner loading pattern.
- Native
<button>semantics: keyboard, focus, and disabled states handled for you. buttonVariantshelper to apply button styling to links and other elements.
Installation
npx shadcn@latest add buttonThis adds button.tsx to @/components/ui. It has no runtime dependency beyond class-variance-authority; composition uses Base UI's render prop.
Usage
import { Button } from "@/components/ui/button"
export function Example() {
return <Button>Click me</Button>
}2
3
4
5
Variants
Set the visual style with the variant prop.
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>2
3
4
5
6
Sizes
Set height and padding with the size prop: default, sm, lg, or icon.
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon" aria-label="Add"><PlusIcon /></Button>2
3
4
With an icon
Place a Lucide icon beside the label. The component spaces and sizes SVG children automatically, on either side of the text.
import { Mail, ArrowRight } from "lucide-react"
<Button>
<Mail /> Email
</Button>
<Button variant="outline">
Continue <ArrowRight />
</Button>2
3
4
5
6
7
8
Loading state
There is no loading prop. Disable the button and render a spinner so the label stays visible for context and screen readers.
import { Loader2 } from "lucide-react"
<Button disabled>
<Loader2 className="animate-spin" /> Please wait
</Button>2
3
4
5
As a link
To render the button as an anchor or a framework Link while keeping its styles, use the render prop. Set nativeButton={false} when the render element is not a <button>:
import Link from "next/link"
import { Button } from "@/components/ui/button"
<Button render={<a href="/login" />} nativeButton={false}>Login</Button>
<Button render={<Link href="/login" />} nativeButton={false}>Login</Button>2
3
4
5
6
Prefer styling a link directly? Use the exported buttonVariants helper:
import { buttonVariants } from "@/components/ui/button"
<Link className={buttonVariants({ variant: "outline" })} href="/login">
Login
</Link>2
3
4
5
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | default | secondary | destructive | outline | ghost | link | default | Visual style. |
size | default | sm | lg | icon | default | Height and padding. |
render | ReactElement | — | Element to render instead of <button>. |
nativeButton | boolean | true | Set to false when render is not a button element. |
disabled | boolean | false | Disable interaction (use for loading states). |
All other native <button> attributes (onClick, type, aria-*, …) are forwarded.
Accessibility
- Renders a native
<button>, so it is focusable and keyboard-operable (Enter / Space) by default. - Give icon-only buttons an
aria-label, since there is no visible text for screen readers otherwise. - Keep the label present during loading (disable instead of replacing text) so the button's purpose stays announced.
When to use the Button
Reach for the Button for actions the user triggers directly: form submissions, dialog confirmations, navigation triggers, toolbar controls, and destructive actions. It is the workhorse of dashboards, admin panels, checkout flows, and SaaS interfaces.
A few rules keep an interface readable:
- Use the
defaultvariant for the single most important action in a view, and keep it to one per section. Usesecondary,outline, orghostfor lower-priority actions. - Reserve
destructivefor irreversible actions like delete, and pair it with a Dialog confirmation. - For navigation that changes the URL, render the Button as a link so it stays a real anchor.
- For state that toggles on and off (filters, formatting, view switches), a Toggle is usually a better fit than a Button.
- Group several related actions with a Button Group, and combine buttons with a Form and Input for submission flows.
Used in these blocks
See the Button in finished, production-ready layouts:
- Pricing sections: plan CTAs and toggles.
- CTA blocks: primary and secondary actions.
- Login forms: submit and social-auth buttons.
- Checkout forms: place-order actions.
Related components
Buttons rarely work alone. Add a Badge for counts or status, a Tooltip for icon-only actions, and a Dropdown Menu for grouped or overflow actions. In forms, pair the Button with Form and Input; in modal flows, with Dialog. Buttons also anchor rows in a Data Table and headers in a Card.
FAQ
What is the shadcn button used for?
The Button triggers user actions inside an interface: form submissions, dialog confirmations, navigation, filters, and workflow controls. It renders a native <button> styled with Tailwind CSS, so it stays accessible and fully customizable.
How do I make a shadcn button a link?
Use the render prop to render an anchor or framework Link with the button's styles (nativeButton={false} when the render element is not a button), or apply buttonVariants() to the link's className.
How do I show a loading state?
Set disabled and render a spinner icon inside the button (e.g. Loader2 with animate-spin). Keep the label so the action stays clear.
How do I create an icon-only button?
Use size="icon" and add an aria-label describing the action.
What variants does the button support?
default, secondary, destructive, outline, ghost, and link, in sizes default, sm, lg, and icon.
Can I customize the styling?
Yes. Edit button.tsx directly (you own the code) or extend the buttonVariants config with your own variants and sizes.