Join our Discord Community

Shadcn Button

React button component with variants and loading states. Built with TypeScript and Tailwind CSS for Next.js applications using class-variance-authority.

Button styling not working?

Join our Discord community for help from other developers.


Ever worked on a team where every developer creates their own button style? Primary here, accent there, padding all over the place. Before you know it, your app has 47 different button styles and users have no idea what's clickable. This shadcn/ui button brings order to the chaos.

Button showcase

All button variants that actually make sense:

Loading component...

Built with class-variance-authority (CVA) for type-safe variants and TypeScript autocompletion. Uses Radix UI Slot so it works with any framework's Link component. Styled with Tailwind CSS because inline styles are where dreams go to die.

npx shadcn@latest add button

Why buttons actually drive action

Here's the thing—buttons aren't just rectangles you click. They're decision points. Every button asks users to commit to something: submit this form, delete that file, buy this product. The wrong button style at the wrong time kills conversions.

Think about it: a subtle ghost button for "Delete Account"? Users might click it by accident. A tiny secondary button for "Checkout"? Say goodbye to sales. Button hierarchy isn't just design—it's psychology.

This free shadcn button handles the visual consistency while you focus on the user journey. Whether you're building forms, modals, or navigation in your JavaScript apps, buttons that look and behave predictably build user confidence.

Common button patterns you'll actually use

Button sizes

From compact to prominent for different contexts:

Loading component...

Icon buttons

Space-efficient buttons for toolbars and actions:

Loading component...

Buttons with icons

Combining text with visual reinforcement:

Loading component...

Loading states

Show progress during async operations:

Loading component...

Features

This free open source button component includes everything you need:

  • TypeScript variants - Type-safe props with full IntelliSense support
  • CVA powered - Consistent variants without the className soup
  • Tailwind CSS utilities - Override anything without !important hacks
  • Radix UI Slot - Works as Link, button, or any clickable element
  • Accessible by default - Keyboard navigation and ARIA support built-in
  • Loading states - Built-in disabled state during async operations
  • Size flexibility - From tiny icon buttons to large CTAs

API Reference

Button Props

PropTypeDefaultDescription
variant"default" | "destructive" | "outline" | "secondary" | "ghost" | "link""default"Visual style variant
size"default" | "sm" | "lg" | "icon""default"Button size preset
asChildbooleanfalseRender as child component
disabledbooleanfalseDisabled state

Variant Guide

VariantWhen to UseExample
defaultPrimary actionsSave, Submit, Continue
secondaryAlternative actionsCancel, Back, Reset
destructiveDangerous actionsDelete, Remove, Destroy
outlineOn colored backgroundsCards, headers, sections
ghostMinimal emphasisClose, More options
linkNavigation styled as linkLearn more, View details

Production tips

Primary buttons should be obvious. This free shadcn/ui button defaults to a solid color for a reason. Users should instantly know what the main action is. One primary button per view—not three, not five, ONE.

Ghost buttons aren't invisible. They still need proper contrast and hover states. Your React button might look cool all subtle, but if users can't find it, it's useless. Test on actual screens, not just your monitor.

Loading states prevent rage clicks. When users click "Submit" and nothing happens, they click again. And again. Show a spinner, disable the button, do something. This TypeScript component makes it easy—just toggle disabled during async operations.

Touch targets matter on mobile. That tiny "sm" size looks great on desktop. On mobile? Good luck hitting it with a thumb. Minimum 44px height for touch—this open source shadcn component handles it with proper size variants.

Test button text at different widths. "OK" fits everywhere. "Download Financial Report for Q3 2024" doesn't. Your Next.js app needs buttons that handle both. Set max-widths or let text wrap—just plan for it.

Integration with other components

Buttons are the workhorses of your React applications. Use them in Form components for submissions and resets. Inside Dialog components, buttons handle confirmations and cancellations.

Combine buttons with DropdownMenu triggers for action menus. This open source pattern keeps interfaces clean while providing options. In Card components, buttons drive the primary actions—view details, add to cart, whatever moves users forward.

For navigation, use asChild with your framework's Link component. Your JavaScript button looks like a button but navigates like a link. Best of both worlds.

Questions you might have


CLAUDE AI GUIDE: Creating Button Components

This section provides a complete step-by-step process for creating custom button components in the shadcn.io monorepo. Follow this guide exactly when given any button code to transform into a proper shadcn component package.

Complete Creation Process

Step 1: Create Package Structure

Create the package directory and files:

mkdir -p /packages/[button-name]/

Required files:

  • /packages/[button-name]/index.tsx - Main component
  • /packages/[button-name]/package.json - Package config
  • /packages/[button-name]/tsconfig.json - TypeScript config
  • /packages/[button-name]/tailwind.config.js - Only if custom animations needed

Step 2: Package Configuration Template

package.json:

{
  "name": "@repo/[button-name]",
  "description": "[Brief component description]",
  "version": "0.0.0",
  "private": true,
  "dependencies": {
    "@repo/shadcn-ui": "workspace:*",
    "react": "^19.1.0",
    "react-dom": "^19.1.0"
  },
  "devDependencies": {
    "@repo/typescript-config": "workspace:*",
    "@types/react": "^19.1.8",
    "@types/react-dom": "^19.1.6",
    "typescript": "^5.8.3"
  }
}

tsconfig.json:

{
  "extends": "@repo/typescript-config/nextjs.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@repo/*": ["../*"],
      "@/components/*": ["../shadcn-ui/components/*"],
      "@/lib/*": ["../shadcn-ui/lib/*"]
    }
  },
  "include": ["**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

Step 3: Component Implementation Pattern

index.tsx template:

'use client';

import React from 'react';
import { cn } from '@repo/shadcn-ui/lib/utils';

export interface [ComponentName]Props
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  // Add specific props here
}

export const [ComponentName] = React.forwardRef<
  HTMLButtonElement,
  [ComponentName]Props
>(({
  // Props with defaults
  className,
  style,
  ...props
}, ref) => {
  return (
    <button
      ref={ref}
      className={cn(
        "base-classes-here",
        className
      )}
      style={style}
      {...props}
    >
      {/* Component content */}
    </button>
  );
});

[ComponentName].displayName = "[ComponentName]";

Critical Rules for Component Implementation:

  • Always use 'use client' directive
  • Use forwardRef pattern with proper types
  • Include className and style props for customization
  • Use cn() utility from shadcn-ui for className merging
  • Prefer inline styles over complex Tailwind arbitrary values for critical styling
  • Use theme-aware colors (avoid hardcoded white/black)
  • Keep component focused - only essential props, no overengineering

Step 4: Add to Documentation App

Add dependency to /apps/docs/package.json in alphabetical order:

"@repo/[button-name]": "workspace:*",

Step 5: Create Example File

Create /apps/docs/examples/button/[button-name].tsx:

import { [ComponentName] } from "@repo/[button-name]"

export default function [ExampleName]() {
  return (
    <div className="w-full p-6 flex justify-center">
      <[ComponentName] />
    </div>
  )
}

Step 6: MDX Documentation Structure

Create /apps/docs/content/button/[button-name].mdx following this EXACT template:

---
title: React [Button Name] Button  
description: [Concise description with primary functionality]. [Unique selling point] with React, Next.js, TypeScript, and shadcn/ui integration.
icon: [LucideIconName]
component: true
---

<PoweredBy
  packages={[
    { name: "React", url: "https://react.dev" },
    { name: "[Key Technology]", url: "https://tech-url.com" },
  ]}
/>

<Callout title="[Context-specific callout title]?">
  [Join our Discord community](https://discord.com/invite/Z9NVtNE7bj) for help
  from other developers.
</Callout>

<br />

[ENGAGING INTRODUCTION PARAGRAPH]
Use conversational tone. Start with a relatable scenario or problem. Explain why this button is different from boring alternatives. Use specific examples and developer pain points.

### [Descriptive action title]

[Brief explanation of the main feature]:

<Preview path="button/[button-name]" />

Built for React applications with TypeScript and Next.js. [Technical implementation details]. [Performance characteristics]. [Integration benefits with shadcn/ui].

## Installation  

<Installer packageName="[button-name]" />

## Why [problem this solves]

[2-3 paragraphs explaining the user experience problem this button solves. Use conversational tone, specific examples, and relate to real development scenarios. Mention technical implementation benefits.]

## Features

- **[Core functionality]** with [technical implementation detail]
- **[Key feature 2]** using [specific technology or approach]  
- **[Performance aspect]** with [optimization detail]
- **TypeScript support** with complete interface definitions
- **shadcn/ui integration** using design tokens and utilities
- **[Additional feature]** for [practical benefit]

## Examples (ONLY if you have truly different variants)

### [Variant Name]

<Preview path="button/[button-name]-[variant]" />

[Brief explanation of what makes this variant different]

## API Reference

### [ComponentName]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `prop1` | `Type` | `"default"` | Clear description |
| `prop2` | `Type` | **required** | Required prop description |
| `className` | `string` | - | Additional CSS classes |

### [Additional sections if needed]
- Color variants, animation options, etc.

## Common gotchas

**[Issue 1]**: [Clear explanation and solution]

**[Issue 2]**: [Clear explanation and solution]

## You might also like

<Cards>
  <Card
    href="/button/[related-button]"
    title="[Related Button]"
    description="[Brief description]"
  />
</Cards>

## Questions developers actually ask

<Accordions type="single">
  <Accordion id="question-id" title="[Practical question]?">
    [Direct, helpful answer with code examples if needed]
  </Accordion>
  
  <Accordion id="question-id-2" title="[Another common question]?">
    [Direct, helpful answer]
  </Accordion>
</Accordions>

Critical Writing Guidelines

Tone & Voice

  • Conversational, not formal - Write like talking to a fellow developer
  • Problem-first approach - Start with pain points developers face
  • Specific examples - "47 different button styles" not "many button styles"
  • Avoid marketing fluff - No "revolutionary" or "cutting-edge"
  • Developer-focused - Technical details, implementation notes, gotchas

Content Requirements

  • Engaging intro - Hook with relatable scenario, not dry description
  • Technical depth - Explain implementation, not just usage
  • Real problems - Address actual developer pain points
  • SEO keywords naturally integrated: react, nextjs, shadcn, typescript, tailwind, component, open source, free
  • Practical examples - Working code, not abstract concepts

Structure Rules

  • Never duplicate previews - Main preview at top is enough for basic usage
  • Examples section only for different variants - Don't repeat the same thing
  • API Reference must match actual component - Check props interface carefully
  • Questions should be practical - What developers actually ask, not theoretical

Validation Checklist

  • Component name matches throughout (PascalCase)
  • Props table matches actual component interface
  • Default values are accurate
  • Preview path exists: /apps/docs/examples/button/[name].tsx
  • Package dependency added to docs package.json
  • Installation uses <Installer packageName="[name]" />
  • Conversational tone throughout
  • SEO keywords naturally integrated
  • Technical implementation details included
  • Common gotchas section addresses real issues

File Locations Summary

  • Package: /packages/[button-name]/
  • Example: /apps/docs/examples/button/[button-name].tsx
  • MDX: /apps/docs/content/button/[button-name].mdx
  • Dependency: /apps/docs/package.json

Registry Integration

The registry system automatically:

  • Transforms imports for user installations
  • Extracts dependencies from package.json
  • Generates installation JSON endpoint
  • Handles Tailwind config parsing (if present)
  • Provides npx shadcn add [button-name] functionality

Follow this guide exactly for consistent, high-quality button component creation that integrates seamlessly with the shadcn.io ecosystem.