Skip to main content

shadcn/ui vs Radix UI vs Headless UI

·StarterPick Team
shadcnradix-uiheadless-uicomponents2026

TL;DR

shadcn/ui is the default in 2026 for good reason: accessible components you own completely, built on Radix UI primitives, styled with Tailwind, and incrementally adoptable. Radix UI is what shadcn/ui is built on — use it if you want to build your own design system from primitives. Headless UI is Tailwind Labs' offering, smaller component selection, less maintained than the alternatives.

The Landscape

LibraryModelStyled?AccessibleComponentsBundle
shadcn/uiCopy-paste✅ Tailwind50+You own it
Radix UInpm package❌ Unstyled35+~40KB
Headless UInpm package❌ Unstyled10~20KB
Chakra UInpm package60+~100KB
MUInpm package100+~300KB

shadcn/ui: The Copy-Paste Revolution

shadcn/ui isn't a component library in the traditional sense — you don't install it as a dependency. You copy the components into your project.

# Install CLI
npx shadcn@latest init

# Add individual components — copies source code to your project
npx shadcn@latest add button
npx shadcn@latest add dialog
npx shadcn@latest add form
npx shadcn@latest add data-table
npx shadcn@latest add command  # Command palette (cmdk)

The components land in components/ui/ as source files you fully own and modify.

Why This Model Wins

// components/ui/button.tsx — it's yours, edit freely
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
        outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2",
        sm: "h-8 rounded-md px-3 text-xs",
        lg: "h-10 rounded-md px-8",
        icon: "h-9 w-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

Need a loading variant? Add it. Need different sizes? Edit the CVA config. No fighting with library internals, no !important overrides, no waiting for a PR to merge.

shadcn/ui Theming

shadcn/ui uses CSS variables for theming — swap the entire design system by changing variables:

/* globals.css — light theme */
:root {
  --background: 0 0% 100%;
  --foreground: 240 10% 3.9%;
  --primary: 240 5.9% 10%;
  --primary-foreground: 0 0% 98%;
  --secondary: 240 4.8% 95.9%;
  /* ... */
}

/* Dark mode */
.dark {
  --background: 240 10% 3.9%;
  --foreground: 0 0% 98%;
  --primary: 0 0% 98%;
  --primary-foreground: 240 5.9% 10%;
  /* ... */
}

Change --primary to your brand color and every button, badge, and ring updates.


Radix UI: The Primitives Layer

Radix UI provides unstyled, accessible component primitives. shadcn/ui is built on top of Radix — when you use shadcn, you're using Radix underneath.

// Using Radix UI directly — maximum control, no styles
import * as Dialog from '@radix-ui/react-dialog';

function MyDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger asChild>
        <button className="my-trigger-styles">Open</button>
      </Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Overlay className="my-overlay-styles" />
        <Dialog.Content className="my-content-styles">
          <Dialog.Title>Dialog Title</Dialog.Title>
          <Dialog.Description>Dialog content here</Dialog.Description>
          <Dialog.Close>Close</Dialog.Close>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}

When to Use Radix UI Directly

  • Building a custom design system that doesn't use Tailwind
  • White-label product where the styling must be fully custom
  • Large team with a dedicated design system engineer
  • Using CSS-in-JS (Emotion, styled-components) instead of Tailwind

For most SaaS projects, shadcn/ui wraps Radix perfectly — use Radix directly only if shadcn's Tailwind styling doesn't fit your constraints.


Headless UI: Tailwind Labs' Component Library

Headless UI is maintained by the Tailwind CSS team. Fewer components than Radix, but well-integrated with Tailwind.

import { Dialog, Transition } from '@headlessui/react';
import { Fragment } from 'react';

function MyDialog({ isOpen, onClose }) {
  return (
    <Transition appear show={isOpen} as={Fragment}>
      <Dialog as="div" className="relative z-10" onClose={onClose}>
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
        >
          <div className="fixed inset-0 bg-black/25" />
        </Transition.Child>
        <div className="fixed inset-0 overflow-y-auto">
          <Dialog.Panel className="bg-white rounded-xl p-6">
            <Dialog.Title>My Dialog</Dialog.Title>
            {/* content */}
          </Dialog.Panel>
        </div>
      </Dialog>
    </Transition>
  );
}

Headless UI in 2026

Headless UI has fallen behind Radix UI in component coverage and maintenance velocity. The component count (10 vs Radix's 35+) limits it for full SaaS builds. Tailwind UI (the paid design kit) uses it — if you've purchased Tailwind UI, you're already using Headless UI and it integrates perfectly.

Choose Headless UI if: You've purchased Tailwind UI and want components consistent with the design kit.


Boilerplate Defaults

BoilerplateComponent Library
ShipFastshadcn/ui
Supastartershadcn/ui
Makerkitshadcn/ui
T3 Stackshadcn/ui (community)
Epic StackRadix UI (custom)
Open SaaSshadcn/ui

Recommendation

For a new SaaS in 2026:

  1. shadcn/ui — default choice. Best DX, most maintained, boilerplate ecosystem support.
  2. Radix UI directly — only if you're building a fully custom design system.
  3. Headless UI — only if using Tailwind UI's design kit.
  4. Chakra/MUI — consider if switching from React-ecosystem-lock-in is important, or if your team already knows them well.

Find boilerplates by UI library on StarterPick.

Check out this boilerplate

View shadcn/ui on StarterPick →

Comments