Dependencies
tailwindcssshadcn/uilucide-react
shadcn/ui components
npx shadcn@latest add button

The Button component is the most used element in any React application. shadcn/ui's Button gives you a solid foundation — all variants, sizes and states handled — that you own and can customise directly in your codebase.

Installation

# Initialize shadcn/ui if you haven't already
npx shadcn@latest init

# Add the Button component
npx shadcn@latest add button

This creates components/ui/button.tsx in your project. Unlike npm packages, you own this file — modify it however you need.

Usage

import { Button } from "@/components/ui/button"

export default function MyPage() {
  return <Button>Click me</Button>
}

Props Reference

PropTypeDefaultDescription
variantdefault | secondary | destructive | outline | ghost | linkdefaultVisual style
sizedefault | sm | lg | icondefaultButton size
disabledbooleanfalseDisables the button
asChildbooleanfalseRenders as child element (useful for links)
classNamestringAdditional Tailwind classes

1. Button Variants

All shadcn/ui button variants — default, secondary, destructive, outline, ghost and link.

tsx
import { Button } from "@/components/ui/button"

export default function ButtonVariants() {
  return (
    <div className="flex flex-wrap gap-3">
      <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>
    </div>
  )
}

2. Button Sizes

shadcn/ui buttons come in sm, default and lg sizes. Use size prop to control.

tsx
import { Button } from "@/components/ui/button"

export default function ButtonSizes() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button size="sm">Small</Button>
      <Button>Default</Button>
      <Button size="lg">Large</Button>
      <Button size="icon" aria-label="Settings">
        ⚙️
      </Button>
    </div>
  )
}

3. Buttons with Icons

Buttons with Lucide React icons. Use gap-2 to space the icon and text.

tsx
import { Button } from "@/components/ui/button"
import { Download, Trash2, Plus, ArrowRight, Github } from "lucide-react"

export default function ButtonWithIcons() {
  return (
    <div className="flex flex-wrap gap-3">
      <Button>
        <Download className="mr-2 h-4 w-4" />
        Download
      </Button>

      <Button variant="destructive">
        <Trash2 className="mr-2 h-4 w-4" />
        Delete
      </Button>

      <Button variant="outline">
        <Plus className="mr-2 h-4 w-4" />
        Add New
      </Button>

      <Button variant="secondary">
        Continue
        <ArrowRight className="ml-2 h-4 w-4" />
      </Button>

      <Button variant="ghost" className="gap-2">
        <Github className="h-4 w-4" />
        GitHub
      </Button>
    </div>
  )
}

4. Button States

Disabled and loading button states. Use disabled prop or add a spinner for loading.

tsx
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Loader2 } from "lucide-react"

export default function ButtonStates() {
  const [loading, setLoading] = useState(false)

  const handleClick = () => {
    setLoading(true)
    setTimeout(() => setLoading(false), 2000)
  }

  return (
    <div className="flex flex-wrap gap-3">
      {/* Disabled */}
      <Button disabled>Disabled</Button>
      <Button variant="outline" disabled>Disabled Outline</Button>

      {/* Loading with spinner */}
      <Button onClick={handleClick} disabled={loading}>
        {loading ? (
          <>
            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
            Saving...
          </>
        ) : (
          "Save Changes"
        )}
      </Button>

      {/* Always loading */}
      <Button variant="secondary" disabled>
        <Loader2 className="mr-2 h-4 w-4 animate-spin" />
        Processing
      </Button>
    </div>
  )
}

5. Custom Tailwind Buttons

Buttons built with pure Tailwind CSS classes — no shadcn/ui required. Full control over styling.

tsx
export default function TailwindButtons() {
  return (
    <div className="flex flex-wrap gap-3">
      {/* Brand red button */}
      <button className="bg-[#fd4766] hover:bg-[#e03355] text-white font-semibold px-4 py-2 rounded-md transition-colors">
        Brand Button
      </button>

      {/* Gradient button */}
      <button className="bg-gradient-to-r from-[#fd4766] to-[#4f6ef7] hover:opacity-90 text-white font-semibold px-4 py-2 rounded-md transition-opacity">
        Gradient
      </button>

      {/* Pill button */}
      <button className="bg-slate-900 hover:bg-slate-700 text-white font-medium px-6 py-2 rounded-full transition-colors">
        Pill Button
      </button>

      {/* Outline custom */}
      <button className="border-2 border-[#fd4766] text-[#fd4766] hover:bg-[#fd4766] hover:text-white font-medium px-4 py-2 rounded-md transition-all">
        Custom Outline
      </button>

      {/* Ghost with hover */}
      <button className="text-slate-600 hover:text-slate-900 hover:bg-slate-100 font-medium px-4 py-2 rounded-md transition-all">
        Ghost
      </button>
    </div>
  )
}
Tailwind CSS only variant (no shadcn)
tsx
/* Pure Tailwind — no dependencies needed */
export default function PureTailwindButton() {
  return (
    <button className="
      inline-flex items-center justify-center
      bg-[#fd4766] hover:bg-[#e03355]
      text-white font-semibold
      px-4 py-2 rounded-md
      transition-colors duration-200
      focus:outline-none focus:ring-2 focus:ring-[#fd4766] focus:ring-offset-2
      disabled:opacity-50 disabled:cursor-not-allowed
    ">
      Click me
    </button>
  )
}

Frequently Asked Questions

Run npx shadcn@latest init to set up shadcn/ui, then npx shadcn@latest add button to add the Button component. This creates a button.tsx file in your components/ui folder that you can customise directly.
shadcn/ui uses CSS variables for theming. Edit your globals.css to change --primary for the default button color. Or add a custom variant in button.tsx using the buttonVariants cva definition. You can also pass className to override styles on individual buttons.
Add className='w-full' to the Button component: <Button className='w-full'>Submit</Button>. This overrides the default inline-flex display with a full width block button.
Import Loader2 from lucide-react and render it with animate-spin when loading: <Button disabled={loading}>{loading ? <><Loader2 className='mr-2 h-4 w-4 animate-spin'/>Loading...</> : 'Submit'}</Button>. Always disable the button while loading to prevent double submissions.
shadcn/ui Button is a pre-built component with all variants, sizes, focus rings and accessibility built in. A plain Tailwind button is just CSS classes you compose yourself. shadcn/ui is faster for standard use cases. Plain Tailwind gives you more control for unique designs.

Need a Full React + Next.js Dashboard Template?

Get a complete React + Next.js dashboard with 50+ components — built by the same team behind BootstrapPlanet.

Browse Templates →

Use code FIRST30 for 30% off.

Related Components