Skip to main content

Best Tailwind v4 Component Libraries 2026

·PkgPulse Team
0

TL;DR

Tailwind v4 is a fundamental redesign — CSS variables for everything, no tailwind.config.js, @theme directive instead of extend. Most component libraries are in the process of migrating. Shadcn/ui has Tailwind v4 support via shadcn@latest. DaisyUI v5 is the first major library with full v4 support. HeadlessUI v2 from Tailwind Labs is fully compatible. The key v4 change: all theme values are CSS custom properties, making runtime theming trivially easy.

Key Takeaways

  • Tailwind v4 core change: @theme { --color-primary: oklch(...) } replaces tailwind.config.js
  • Migration: npx @tailwindcss/upgrade CLI handles most of the work automatically
  • Shadcn/ui: Full v4 support via npx shadcn@latest init (detects v4 automatically)
  • DaisyUI v5: First major library with native v4 support — @plugin "daisyui" syntax
  • HeadlessUI v2: Tailwind Labs' own headless library, fully v4 compatible
  • Performance: 2-5x faster builds in v4 (Lightning CSS instead of PostCSS)

What Changed in Tailwind v4

Tailwind v4 is not a minor version bump — it's a rethink of how configuration works. In v3, all customization lived in a JavaScript config file. In v4, the config moves into your CSS file using a @theme directive, and all theme values become CSS custom properties. This architectural shift has large implications for how component libraries integrate with your design system.

The three directives from v3 (@tailwind base; @tailwind components; @tailwind utilities;) are replaced by a single @import "tailwindcss". PostCSS is optional — Tailwind v4 ships a Vite plugin (@tailwindcss/vite) that's significantly faster for build-tool-integrated workflows.

/* Tailwind v3 — three separate directives, JS config */
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

/* tailwind.config.ts */
/* module.exports = {
  theme: {
    extend: {
      colors: { primary: '#3b82f6' },
      fontFamily: { sans: ['Inter', 'ui-sans-serif'] },
    }
  }
} */
/* Tailwind v4 — single import, config in CSS */
@import "tailwindcss";

@theme {
  /* Colors using oklch for better perceptual uniformity */
  --color-primary: oklch(0.637 0.237 264.376);
  --color-primary-foreground: oklch(0.99 0.01 0);
  --color-background: oklch(0.99 0.01 0);
  --color-foreground: oklch(0.141 0.005 285.823);

  /* Fonts */
  --font-sans: "Inter Variable", ui-sans-serif, system-ui;
  --font-mono: "Fira Code", ui-monospace;

  /* Border radius */
  --radius: 0.625rem;
}

/* Dark mode now uses @variant instead of darkMode: 'class' config */
@variant dark {
  --color-background: oklch(0.141 0.005 285.823);
  --color-foreground: oklch(0.99 0.01 0);
}

Because all theme values are now CSS custom properties, you can change your entire color scheme at runtime with a single property update — no JavaScript required, no class toggling, just document.documentElement.style.setProperty('--color-primary', newColor). This also means themes can be defined in plain CSS and swapped by loading a different stylesheet.

Setting Up Tailwind v4 with Vite

npm install tailwindcss @tailwindcss/vite
// vite.config.ts
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [
    tailwindcss(),
  ],
});

That's the entire setup. No tailwind.config.js, no PostCSS config, no separate postcss.config.js file. The Vite plugin handles content scanning automatically by analyzing your imports.


shadcn/ui on Tailwind v4

shadcn/ui is not a traditional npm library — components are copied into your components/ui/ directory, giving you full ownership of the code. This copy-paste model means there's no versioning friction; when Tailwind v4 support landed, the components themselves updated to use v4's CSS variable syntax.

shadcn/ui is built on Radix UI primitives (accessible, unstyled) with Tailwind utility classes applied on top. The result is polished, accessible components that you can modify freely because you own the source.

# shadcn/ui automatically detects Tailwind v4
npx shadcn@latest init

# Add components
npx shadcn@latest add button
npx shadcn@latest add dialog
npx shadcn@latest add form
/* globals.css — shadcn/ui theme in Tailwind v4 style */
@import "tailwindcss";

@theme inline {
  /* Light mode tokens */
  --color-background: oklch(1 0 0);
  --color-foreground: oklch(0.145 0 0);
  --color-primary: oklch(0.205 0 0);
  --color-primary-foreground: oklch(0.985 0 0);
  --color-secondary: oklch(0.97 0 0);
  --color-secondary-foreground: oklch(0.205 0 0);
  --color-muted: oklch(0.97 0 0);
  --color-muted-foreground: oklch(0.556 0 0);
  --color-accent: oklch(0.97 0 0);
  --color-accent-foreground: oklch(0.205 0 0);
  --color-destructive: oklch(0.577 0.245 27.325);
  --color-border: oklch(0.922 0 0);
  --color-input: oklch(0.922 0 0);
  --color-ring: oklch(0.708 0 0);
  --radius: 0.625rem;
}

@variant dark {
  --color-background: oklch(0.145 0 0);
  --color-foreground: oklch(0.985 0 0);
  --color-border: oklch(1 0 0 / 15%);
  --color-muted-foreground: oklch(0.708 0 0);
  /* ... rest of dark tokens */
}

A Button component in shadcn/ui uses these CSS variables via Tailwind utility classes. When you copy the component into your project, it looks like this:

// components/ui/button.tsx — shadcn/ui button with v4 CSS variable tokens
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';

const buttonVariants = cva(
  // Base classes using v4 CSS variable-backed utilities
  '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-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
  {
    variants: {
      variant: {
        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
        destructive: 'bg-destructive text-white hover:bg-destructive/90',
        outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
        secondary: 'bg-secondary text-secondary-foreground 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' },
  }
);

export function Button({ className, variant, size, ...props }) {
  return (
    <button className={cn(buttonVariants({ variant, size }), className)} {...props} />
  );
}

DaisyUI v5: First v4-Native Library

DaisyUI v5 is the first major component library with native Tailwind v4 support. Rather than copying components, DaisyUI works as a plugin that adds semantic HTML class names (btn, card, badge, modal, alert) on top of Tailwind. The classes work with plain HTML — no JavaScript required for most components.

npm install -D daisyui@latest
/* globals.css — DaisyUI v5 with Tailwind v4 */
@import "tailwindcss";
@plugin "daisyui";

/* Optional: configure themes inline */
@plugin "daisyui" {
  themes: light dark cupcake emerald;
  darkTheme: dark;
  base: true;
  styled: true;
  utils: true;
}

The @plugin "daisyui" syntax replaces v4's plugins: [require('daisyui')] from tailwind.config.js. DaisyUI v5 provides 50+ semantic component classes that map to Tailwind utilities under the hood.

<!-- DaisyUI v5 — Card with Badge and Actions -->
<div class="card bg-base-100 shadow-xl w-96">
  <figure>
    <img src="/cover.jpg" alt="Cover" />
  </figure>
  <div class="card-body">
    <div class="flex items-start justify-between">
      <h2 class="card-title">Package Explorer</h2>
      <div class="badge badge-secondary">New</div>
    </div>
    <p class="text-base-content/70">
      Track npm package health, download trends, and bundle size.
    </p>
    <div class="card-actions justify-end mt-4">
      <button class="btn btn-primary">View Package</button>
      <button class="btn btn-ghost">Learn More</button>
    </div>
  </div>
</div>

<!-- DaisyUI v5 — Alert variants -->
<div role="alert" class="alert alert-success">
  <span>Package published successfully!</span>
</div>
<div role="alert" class="alert alert-error">
  <span>Build failed. Check your dependencies.</span>
</div>

DaisyUI's semantic class approach is particularly useful for server-rendered HTML, documentation sites, and projects where you want component-level classes without framework-specific imports. For React/Vue apps that don't need full accessibility semantics (dialogs, comboboxes, popovers), DaisyUI's class-based approach is often simpler than bringing in a headless component library.


Flowbite: Mid-Migration to v4

Flowbite provides components for React, Vue, Angular, and Svelte via framework-specific packages. As of early 2026, Flowbite is actively migrating to Tailwind v4 — core components work, but some advanced interactive components may require manual class updates.

npm install flowbite flowbite-react
/* CSS — Flowbite v3 with Tailwind v4 partial support */
@import "tailwindcss";
@plugin "flowbite/plugin";

/* Source flowbite-react to ensure component classes are included */
@source "../node_modules/flowbite-react/dist";
// Flowbite React components
import { Button, Card, Modal, TextInput, Label } from 'flowbite-react';
import { useState } from 'react';

function CreateUserModal() {
  const [open, setOpen] = useState(false);

  return (
    <div>
      <Button onClick={() => setOpen(true)}>Create User</Button>
      <Modal show={open} onClose={() => setOpen(false)}>
        <Modal.Header>Create New User</Modal.Header>
        <Modal.Body>
          <div className="space-y-4">
            <div>
              <Label htmlFor="name">Full Name</Label>
              <TextInput id="name" placeholder="Jane Doe" required />
            </div>
            <div>
              <Label htmlFor="email">Email</Label>
              <TextInput id="email" type="email" placeholder="jane@example.com" required />
            </div>
          </div>
        </Modal.Body>
        <Modal.Footer>
          <Button onClick={() => setOpen(false)}>Create User</Button>
          <Button color="gray" onClick={() => setOpen(false)}>Cancel</Button>
        </Modal.Footer>
      </Modal>
    </div>
  );
}

The practical advice for Flowbite: it works well for standard component usage with Tailwind v4, but if you're starting a new project today, consider whether shadcn/ui (for React) or DaisyUI v5 (framework-agnostic) might serve your needs with more complete v4 support.


HeadlessUI v2: Accessible and v4-Compatible

HeadlessUI comes from Tailwind Labs — the same team that builds Tailwind CSS itself. It provides completely unstyled, accessible component primitives: Combobox, Dialog, Listbox, Menu, Popover, RadioGroup, Switch, Tabs, and Transition. You bring the styling via Tailwind classes.

HeadlessUI v2 is fully compatible with Tailwind v4 and represents the best choice when you need ARIA-compliant interactive components without the styling constraints of a pre-styled library.

npm install @headlessui/react
// HeadlessUI v2 — Dialog example with v4 Tailwind classes
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
import { Fragment } from 'react';

interface ConfirmDialogProps {
  isOpen: boolean;
  onClose: () => void;
  onConfirm: () => void;
  title: string;
  description: string;
}

export function ConfirmDialog({
  isOpen,
  onClose,
  onConfirm,
  title,
  description,
}: ConfirmDialogProps) {
  return (
    <Transition appear show={isOpen} as={Fragment}>
      <Dialog as="div" className="relative z-50" onClose={onClose}>
        {/* Backdrop */}
        <TransitionChild
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="fixed inset-0 bg-black/25" />
        </TransitionChild>

        {/* Dialog content */}
        <div className="fixed inset-0 overflow-y-auto">
          <div className="flex min-h-full items-center justify-center p-4">
            <TransitionChild
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 scale-95"
              enterTo="opacity-100 scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 scale-100"
              leaveTo="opacity-0 scale-95"
            >
              <DialogPanel className="w-full max-w-md rounded-2xl bg-background p-6 shadow-xl">
                <DialogTitle className="text-lg font-semibold text-foreground">
                  {title}
                </DialogTitle>
                <p className="mt-2 text-sm text-muted-foreground">
                  {description}
                </p>
                <div className="mt-6 flex justify-end gap-3">
                  <button
                    onClick={onClose}
                    className="rounded-md px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent"
                  >
                    Cancel
                  </button>
                  <button
                    onClick={onConfirm}
                    className="rounded-md bg-destructive px-4 py-2 text-sm font-medium text-white hover:bg-destructive/90"
                  >
                    Confirm
                  </button>
                </div>
              </DialogPanel>
            </TransitionChild>
          </div>
        </div>
      </Dialog>
    </Transition>
  );
}

HeadlessUI is the best choice when you need screen-reader accessibility and keyboard navigation without committing to a particular visual style. The Dialog handles focus trapping and aria attributes correctly. The Combobox handles complex accessible autocomplete behavior. All the hard accessibility engineering is done — you just provide Tailwind classes for the visuals.


Migration Path: v3 to v4

The automated upgrade tool handles most of the migration:

# Run from your project root
npx @tailwindcss/upgrade@next

The upgrade tool converts your tailwind.config.js theme extensions to @theme blocks in your CSS, updates the three-directive setup to a single @import, migrates darkMode: 'class' to @variant dark, updates PostCSS config, and renames classes that changed (for example, shadow becoming shadow-sm in v4's updated scale).

After running the automated migration, there are manual steps to verify:

  1. Custom plugins — The plugin API changed in v4. Plugins using addBase, addComponents, or addUtilities need updates to the new CSS-first approach.
  2. Dark mode toggle logic — If you were toggling a dark class on <html>, that still works. But if you were using darkMode: 'media' for OS-level preference detection, verify the @variant dark behavior matches your expectations.
  3. Third-party class merging — Tools like tailwind-merge and clsx work fine, but if you have custom merge config for extended colors, update those to match the new CSS variable naming.

For existing projects on shadcn/ui v3, running npx shadcn@latest diff after the Tailwind migration will show which components need updates to use v4's CSS variable tokens.


Component Library v4 Support Status

Libraryv4 SupportStyled vs HeadlessFrameworkDownloads
shadcn/uiFullStyled (copy-paste)ReactCopy-paste model
DaisyUI v5FullStyled (semantic CSS)Any (HTML classes)~3M weekly
HeadlessUI v2FullHeadlessReact, Vue~4M weekly
FlowbitePartialStyledReact, Vue, Angular~500K weekly
Preline UIFullStyledAny~100K weekly
Tailwind ElementsNoStyledMultiple~200K weekly

When to Choose

shadcn/ui — React projects where you want full code ownership, Radix-based accessibility, and a design system that aligns with the Next.js ecosystem. The copy-paste model means no upgrade friction and complete customizability. Best for production apps that need polished components they can own.

DaisyUI v5 — Framework-agnostic projects, server-rendered HTML, documentation sites, or any situation where semantic CSS class names are preferable to JSX components. No JavaScript required for most components. Full Tailwind v4 support from the start.

HeadlessUI v2 — When you need complete visual control but don't want to implement keyboard navigation, focus management, and ARIA attributes yourself. Dialog, Combobox, and Menu are the standout use cases. Best used alongside DaisyUI or a custom design system for visual styling.

Flowbite — Existing projects already using Flowbite that are migrating to v4. For new projects, the partial v4 support makes shadcn/ui or DaisyUI more attractive starting points in 2026.


The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.