Button Set: Primary, Secondary, Ghost, Icon and Loading

Eight buttons that cover what most sites and apps need: a primary action, a dark alternative, an outlined secondary, a quiet ghost button, a destructive button, one with a trailing icon, a loading state with a spinner, and a round icon-only button. They share one base class, so sizes and focus rings stay consistent.

Code

export default function ButtonSet() {
  const base = "inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-sm font-semibold transition focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-600 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50";
  return (
    <div className="flex flex-wrap items-center gap-4 bg-white p-10">
      <button type="button" className={`${base} bg-indigo-600 text-white shadow-sm hover:bg-indigo-500 active:bg-indigo-700`}>Primary</button>
      <button type="button" className={`${base} bg-slate-900 text-white hover:bg-slate-700`}>Dark</button>
      <button type="button" className={`${base} border border-slate-300 bg-white text-slate-900 hover:bg-slate-50`}>Secondary</button>
      <button type="button" className={`${base} text-indigo-700 hover:bg-indigo-50`}>Ghost</button>
      <button type="button" className={`${base} bg-rose-600 text-white hover:bg-rose-500`}>Delete</button>
      <button type="button" className={`${base} bg-indigo-600 text-white hover:bg-indigo-500`}>
        Continue
        <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className="h-4 w-4"><path fillRule="evenodd" d="M3 10a.75.75 0 0 1 .75-.75h10.64l-3.2-3.2a.75.75 0 1 1 1.06-1.06l4.5 4.5a.75.75 0 0 1 0 1.06l-4.5 4.5a.75.75 0 1 1-1.06-1.06l3.2-3.2H3.75A.75.75 0 0 1 3 10Z" clipRule="evenodd" /></svg>
      </button>
      <button type="button" disabled className={`${base} bg-indigo-600 text-white`}>
        <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-4 w-4 animate-spin motion-reduce:animate-none"><circle cx="12" cy="12" r="9" stroke="currentColor" strokeOpacity=".3" strokeWidth="3" /><path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" strokeWidth="3" strokeLinecap="round" /></svg>
        Saving
      </button>
      <button type="button" aria-label="Add item" className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-indigo-600 text-white transition hover:bg-indigo-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-600 focus-visible:ring-offset-2">
        <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className="h-5 w-5"><path d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z" /></svg>
      </button>
    </div>
  );
}

Install with one command: npx shadcn@latest add https://we.inc/r/button-set.json

When to use it

How to customize it

Accessibility

Questions

What is the difference between a primary and a secondary button?

The primary button is the one action you most want taken on a screen, so it gets the strongest colour. Secondary and ghost buttons are for other actions such as Cancel or Learn more, and are styled more quietly so they do not compete.

How do I make a Tailwind button show a loading spinner?

Put a small SVG with the `animate-spin` class before the label and set the `disabled` attribute while the request is running. The `disabled:` classes in the base string fade the button and change the cursor.

Should I use a button or a link?

Use a button for actions that change something on the page, such as submit, open or delete. Use a link for navigation to another page or section. Both can share the same visual style.

What size should a button be on mobile?

Aim for a touch target of about 44 by 44 pixels. The default here is 40 pixels tall; use the larger padding from the customise notes for primary actions on phones.

Related: Pricing Table with Monthly and Yearly Toggle, Split Hero with Email Capture.

More buttons or all free UI components. Start for free to have a full site built around it.

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap