Button

You know, those things you click to do just about anything in a web application.

Preview

Basic usage

import { Button } from '@/registry/ui-kit/tailwind-buttons/react'

function Example() {
  return <Button>Save changes</Button>
}

With color variants

Use the variant prop to change the button color.

import { Button } from '@/registry/ui-kit/tailwind-buttons/react'

function Example() {
  return (
    <div className="flex gap-2">
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="success">Success</Button>
      <Button variant="danger">Danger</Button>
    </div>
  )
}

Outline buttons

Use the outline prop for a bordered variant that fills on hover.

import { Button } from '@/registry/ui-kit/tailwind-buttons/react'

function Example() {
  return (
    <div className="flex gap-2">
      <Button outline>Primary</Button>
      <Button variant="danger" outline>Danger</Button>
    </div>
  )
}

With icon

Pass an SVG icon as a child alongside text.

import { Button } from '@/registry/ui-kit/tailwind-buttons/react'
import { Plus, Trash2 } from 'lucide-react'

function Example() {
  return (
    <div className="flex gap-2">
      <Button><Plus size={16} /> New item</Button>
      <Button variant="danger"><Trash2 size={16} /> Delete</Button>
    </div>
  )
}

Icon only

Use the icon prop for a compact square button. Combine with pill for a circle.

import { Button } from '@/registry/ui-kit/tailwind-buttons/react'
import { Plus, Heart, Settings } from 'lucide-react'

function Example() {
  return (
    <div className="flex gap-2">
      <Button icon><Plus size={16} /></Button>
      <Button icon pill><Heart size={16} /></Button>
      <Button icon pill outline><Settings size={16} /></Button>
    </div>
  )
}

Pill buttons

Use the pill prop for fully rounded buttons.

import { Button } from '@/registry/ui-kit/tailwind-buttons/react'

function Example() {
  return <Button pill>Get started</Button>
}

Component API

PropDefaultDescription
variant"primary"The color variant the button should use.
size"md"The size of the button.
outlinefalseRenders an outline style with transparent background.
pillfalseRenders a fully rounded pill shape.
iconfalseRenders a square icon-only button. Combine with pill for a circle.
disabledfalseDisables the button and reduces opacity.
className""Additional CSS classes.

Source Code

"use client";

import { forwardRef } from "react";

type ButtonVariant =
  | "primary"
  | "secondary"
  | "success"
  | "danger"
  | "warning"
  | "info"
  | "light"
  | "dark";

type ButtonSize = "sm" | "md" | "lg";

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  size?: ButtonSize;
  outline?: boolean;
  pill?: boolean;
  icon?: boolean;
  children: React.ReactNode;
}

const solidStyles: Record<ButtonVariant, string> = {
  primary: "bg-indigo-600 text-white hover:bg-indigo-500 shadow-sm shadow-indigo-600/20",
  secondary: "bg-zinc-600 text-white hover:bg-zinc-500 shadow-sm shadow-zinc-600/20",
  success: "bg-emerald-600 text-white hover:bg-emerald-500 shadow-sm shadow-emerald-600/20",
  danger: "bg-rose-600 text-white hover:bg-rose-500 shadow-sm shadow-rose-600/20",
  warning: "bg-amber-500 text-white hover:bg-amber-400 shadow-sm shadow-amber-500/20",
  info: "bg-sky-600 text-white hover:bg-sky-500 shadow-sm shadow-sky-600/20",
  light: "bg-zinc-100 text-zinc-700 hover:bg-zinc-200/80 shadow-sm border border-zinc-200",
  dark: "bg-zinc-900 text-zinc-100 hover:bg-zinc-800 shadow-sm shadow-zinc-900/20",
};

const outlineStyles: Record<ButtonVariant, string> = {
  primary: "border border-indigo-300 text-indigo-600 hover:bg-indigo-50 dark:border-indigo-800 dark:text-indigo-400 dark:hover:bg-indigo-950",
  secondary: "border border-zinc-300 text-zinc-600 hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-400 dark:hover:bg-zinc-900",
  success: "border border-emerald-300 text-emerald-600 hover:bg-emerald-50 dark:border-emerald-800 dark:text-emerald-400 dark:hover:bg-emerald-950",
  danger: "border border-rose-300 text-rose-600 hover:bg-rose-50 dark:border-rose-800 dark:text-rose-400 dark:hover:bg-rose-950",
  warning: "border border-amber-300 text-amber-600 hover:bg-amber-50 dark:border-amber-800 dark:text-amber-400 dark:hover:bg-amber-950",
  info: "border border-sky-300 text-sky-600 hover:bg-sky-50 dark:border-sky-800 dark:text-sky-400 dark:hover:bg-sky-950",
  light: "border border-zinc-200 text-zinc-500 hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-400 dark:hover:bg-zinc-900",
  dark: "border border-zinc-800 text-zinc-800 hover:bg-zinc-900 hover:text-white dark:border-zinc-300 dark:text-zinc-300 dark:hover:bg-zinc-100 dark:hover:text-zinc-900",
};

const sizeStyles: Record<ButtonSize, string> = {
  sm: "px-3 py-1.5 text-sm gap-1.5",
  md: "px-4 py-2 text-sm gap-2",
  lg: "px-5 py-2.5 text-base gap-2",
};

const iconSizeStyles: Record<ButtonSize, string> = {
  sm: "h-8 w-8 p-0",
  md: "h-9 w-9 p-0",
  lg: "h-10 w-10 p-0",
};

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ variant = "primary", size = "md", outline = false, pill = false, icon = false, className = "", children, ...props }, ref) => {
    const shape = icon
      ? pill ? "rounded-full" : "rounded-lg"
      : pill ? "rounded-full" : "rounded-lg";

    return (
      <button
        ref={ref}
        className={`inline-flex items-center justify-center font-medium transition-all duration-150 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 disabled:pointer-events-none disabled:opacity-50 ${shape} ${outline ? outlineStyles[variant] : solidStyles[variant]} ${icon ? iconSizeStyles[size] : sizeStyles[size]} ${className}`}
        {...props}
      >
        {children}
      </button>
    );
  }
);

Button.displayName = "Button";

export { Button };
export type { ButtonProps, ButtonVariant, ButtonSize };
export default Button;