← back to blog
Tutorial2026-05-30· IconSearch Team

Tailwind CSS v4 and Icons — The Complete Integration Guide (2026)

How to use SVG icon libraries with Tailwind CSS v4. Covers the new CSS-first config, className sizing, currentColor, dark mode icons, and which libraries work best with Tailwind v4 in React and Next.js.

#tailwind css v4#icons#react#nextjs#lucide#heroicons#svg#dark mode
// This article was researched and edited by the IconSearch team. Content may be AI-assisted and is reviewed for accuracy.

Tailwind CSS v4 landed as one of the most significant releases in the framework's history. The configuration system moved from JavaScript to CSS. The build engine is up to 5x faster. Zero configuration files are needed for most projects. And almost every developer upgrading to v4 immediately asks the same question: do my icon libraries still work the same way?

The short answer is yes — SVG icon libraries like Lucide, Heroicons, Tabler, and Phosphor all work with Tailwind CSS v4. The longer answer is that v4's changes actually make icon integration cleaner in several ways, while introducing a few new patterns worth knowing.

This guide covers everything you need to use icons correctly with Tailwind CSS v4 in React and Next.js projects — from basic className sizing to dark mode currentColor behavior to the new CSS variable system.

What Changed in Tailwind CSS v4 That Affects Icons

Three v4 changes directly affect how you work with icons.

Change 1 — CSS-first configuration

Tailwind v4 replaces tailwind.config.js with a CSS-first configuration system. Your theme tokens, custom colors, and spacing scales are now defined in your CSS file using @theme:

/* globals.css — Tailwind v4 */
@import "tailwindcss";

@theme {
  --color-brand: #7c6af7;
  --color-brand-hover: #9b8cff;
  --spacing-icon-sm: 16px;
  --spacing-icon-md: 20px;
  --spacing-icon-lg: 24px;
}

For icons, this means you can define consistent icon sizing tokens directly in CSS and use them as Tailwind utilities — size-icon-sm, size-icon-md, size-icon-lg — without touching a JavaScript config file.

Change 2 — Automatic content detection

Tailwind v4 automatically detects your template files without a content array in the config. This means icon component files in node_modules are no longer accidentally included in the content scan, which was a rare source of unexpected class generation in v3. Your icon imports stay clean.

Change 3 — Native CSS cascade layers

Tailwind v4 uses CSS cascade layers (@layer) natively. This affects how you override icon styles with custom CSS. Any custom icon CSS you write now needs to be in the correct layer to take precedence over Tailwind utilities. More on this in the override section below.

Installing Icon Libraries with Tailwind CSS v4

Installation is unchanged from v3. Icon libraries are npm packages — Tailwind's configuration system has no effect on how you install them.

# Lucide (recommended default)
npm install lucide-react

# Heroicons (Tailwind team's own library)
npm install @heroicons/react

# Tabler Icons (largest free library)
npm install @tabler/icons-react

# Phosphor Icons (multi-weight system)
npm install @phosphor-icons/react

The only Tailwind v4 specific consideration is that you no longer need to add icon component paths to a content array since automatic content detection handles this.

Basic Icon Sizing with Tailwind v4

Tailwind v4 keeps the same utility class system for sizing. The size-*, w-*, and h-* utilities all work identically to v3:

import { Home, Settings, Bell, Search } from 'lucide-react'

// Standard sizes using Tailwind utilities
<Home className="size-4" />      {/* 16px */}
<Home className="size-5" />      {/* 20px */}
<Home className="size-6" />      {/* 24px */}
<Home className="size-8" />      {/* 32px */}

// Tailwind v4 introduces size-* as the shorthand for w-* h-* together
// This was backported from v4's design system thinking
<Settings className="size-5 text-gray-500" />
<Bell className="size-6 text-blue-500" />
<Search className="size-4 text-gray-400" />

The size-* utility is the v4-idiomatic way to size icons — it sets both width and height simultaneously, which is exactly what you always want for square icons.

Defining custom icon size tokens in v4:

/* globals.css */
@import "tailwindcss";

@theme {
  /* Define your icon size scale once */
  --size-icon-xs: 12px;
  --size-icon-sm: 16px;
  --size-icon-md: 20px;
  --size-icon-lg: 24px;
  --size-icon-xl: 32px;
}
{/* Now use them as utilities anywhere in your project */}
<Home className="size-icon-sm" />
<Settings className="size-icon-md" />
<Bell className="size-icon-lg" />

This pattern is new to v4 and creates a proper icon size scale that stays consistent across your entire design system — one source of truth in CSS, accessible as utilities everywhere.

Icon Colors with Tailwind CSS v4

Icon coloring works the same as v3 with one important improvement: v4's CSS variable system makes dynamic icon colors much cleaner.

Basic color utilities:

import { Heart, Star, AlertTriangle, CheckCircle } from 'lucide-react'

{/* Semantic colors */}
<CheckCircle className="size-5 text-green-500" />
<AlertTriangle className="size-5 text-yellow-500" />
<Heart className="size-5 text-red-500" />
<Star className="size-5 text-blue-500" />

{/* Muted/secondary icons */}
<Settings className="size-5 text-gray-400" />

{/* Brand color */}
<Home className="size-5 text-violet-600" />

Dynamic icon colors using v4 CSS variables:

@theme {
  --color-icon-primary: var(--color-violet-600);
  --color-icon-secondary: var(--color-gray-400);
  --color-icon-success: var(--color-green-500);
  --color-icon-warning: var(--color-yellow-500);
  --color-icon-danger: var(--color-red-500);
}
{/* Semantic icon color utilities */}
<CheckCircle className="size-5 text-icon-success" />
<AlertTriangle className="size-5 text-icon-warning" />
<Home className="size-5 text-icon-primary" />

This approach gives you a semantic icon color system — when your brand color changes, you update one CSS variable and every text-icon-primary icon updates automatically.

currentColor and SVG Icon Inheritance in v4

currentColor is the mechanism that makes icon coloring via Tailwind work at all. Every major icon library uses currentColor as the default stroke and fill value, which means the icon inherits whatever color the CSS color property is set to.

Tailwind's text-* utilities set the CSS color property. When you write className="text-red-500", Tailwind sets color: #ef4444. The SVG icon reads currentColor for its stroke and renders red.

{/* This chain: text-red-500 → color: #ef4444 → currentColor → icon renders red */}
<Heart className="size-5 text-red-500" />

{/* You can also inherit color from a parent */}
<button className="text-blue-600 hover:text-blue-700 flex items-center gap-2">
  <Home className="size-4" /> {/* Inherits blue-600, turns blue-700 on hover */}
  Home
</button>

The currentColor inheritance pattern in v4 components:

function NavItem({ icon: Icon, label, active }: {
  icon: React.ComponentType<{ className?: string }>
  label: string
  active?: boolean
}) {
  return (
    <a className={`
      flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium
      ${active 
        ? 'text-violet-600 bg-violet-50' 
        : 'text-gray-500 hover:text-gray-900 hover:bg-gray-50'
      }
    `}>
      {/* Icon inherits text color from parent — no explicit color needed */}
      <Icon className="size-4" />
      {label}
    </a>
  )
}

This pattern is the cleanest way to handle icon coloring in components — the parent sets the color, the icon inherits it. Active states, hover states, and disabled states all propagate to the icon automatically without any additional className logic on the icon itself.

Dark Mode Icons with Tailwind CSS v4

Tailwind v4 changes how dark mode configuration works. The darkMode key in tailwind.config.js is gone — dark mode is configured in CSS:

/* globals.css — v4 dark mode configuration */
@import "tailwindcss";

@variant dark (&:where(.dark, .dark *));

Or for system preference dark mode:

@variant dark (&:where(@media(prefers-color-scheme:dark), @media(prefers-color-scheme:dark) *));

Icon dark mode works the same as v3 — use the dark: variant:

{/* Icon changes color in dark mode */}
<Home className="size-5 text-gray-600 dark:text-gray-300" />
<Settings className="size-5 text-gray-500 dark:text-gray-400" />
<Bell className="size-5 text-gray-700 dark:text-gray-200" />

The recommended pattern — define dark mode icon colors as CSS variables:

@theme {
  --color-icon-default: var(--color-gray-600);
  --color-icon-muted: var(--color-gray-400);
}

@variant dark {
  @theme {
    --color-icon-default: var(--color-gray-300);
    --color-icon-muted: var(--color-gray-500);
  }
}
{/* No dark: variant needed — the CSS variable handles it */}
<Home className="size-5 text-icon-default" />
<Settings className="size-5 text-icon-muted" />

This approach removes dark mode class clutter from your JSX entirely. The icon color adapts automatically because the underlying CSS variable changes in dark mode.

For a deeper dive into dark mode icon implementation including the Next.js hydration flash bug fix, see the [SVG icons dark mode guide](/blog/svg-icons-dark-mode-react-nextjs-2026) on this site.

Heroicons — The Most Tailwind-Native Icon Library

Heroicons is made by the Tailwind Labs team — the same people who build Tailwind CSS. This means Heroicons and Tailwind v4 are designed to work together, and the Heroicons Figma kit uses the same design tokens as Tailwind UI.

npm install @heroicons/react
import { HomeIcon, Cog6ToothIcon, BellIcon } from '@heroicons/react/24/outline'
import { HomeIcon as HomeSolid } from '@heroicons/react/24/solid'

{/* Heroicons with Tailwind v4 sizing */}
<HomeIcon className="size-6 text-gray-600 dark:text-gray-300" />
<Cog6ToothIcon className="size-5 text-gray-500" />

{/* Solid variant for filled state */}
<HomeSolid className="size-6 text-violet-600" />

Heroicons ships in three size variants — 16px (mini), 20px (small), and 24px (default) — which map directly to Tailwind's size-4, size-5, and size-6 utilities. This is intentional design by the Tailwind team — no arbitrary sizing needed.

Compare Heroicons with Lucide and other options at [iconsearch.info/compare/lucide-icons-vs-heroicons](/compare/lucide-icons-vs-heroicons).

Lucide Icons with Tailwind CSS v4

Lucide is the most popular React icon library in 2026 and the default in shadcn/ui. It works perfectly with Tailwind v4 and is the recommended choice for most Next.js projects.

npm install lucide-react
import { Home, Settings, Bell, Search, ChevronDown } from 'lucide-react'

{/* Lucide with Tailwind v4 */}
<Home className="size-5 text-gray-600" />
<Settings className="size-4 text-gray-500 hover:text-gray-900 transition-colors" />

{/* Lucide supports strokeWidth prop for design consistency */}
<Home className="size-6 text-violet-600" strokeWidth={1.5} />

{/* Combining Tailwind v4 utilities with Lucide props */}
<Bell className="size-5 text-gray-500 dark:text-gray-400" strokeWidth={2} />

The strokeWidth prop is Lucide-specific and controls the icon's visual weight. The default is 2, but 1.5 gives a lighter feel for marketing pages and 2.5 gives a bolder feel for dense UIs. There is no Tailwind utility for stroke width — you use the prop directly.

For the complete Lucide guide including all available props and TypeScript types, see [Lucide Icons Complete Guide 2026](/blog/lucide-icons-complete-guide-2026).

Tabler Icons with Tailwind CSS v4

Tabler Icons has 5,900+ MIT-licensed icons — the largest free library available. It integrates with Tailwind v4 exactly like Lucide:

npm install @tabler/icons-react
import { IconHome, IconSettings, IconBell } from '@tabler/icons-react'

{/* Tabler with Tailwind v4 */}
<IconHome className="size-6 text-gray-700" />
<IconSettings className="size-5 text-gray-500 hover:rotate-90 transition-transform duration-300" />
<IconBell className="size-5 text-gray-600 dark:text-gray-300" />

Tabler's size and stroke props work alongside Tailwind utilities:

{/* Tabler props + Tailwind utilities together */}
<IconHome size={20} stroke={1.5} className="text-violet-600" />

Use Tabler when Lucide's 1,500 icons do not cover your use case. See the [Lucide vs Tabler comparison](/compare/lucide-icons-vs-tabler-icons) for a full breakdown of when to choose each.

Icon Button Pattern with Tailwind CSS v4

Icon buttons are the most common icon use case. Tailwind v4's utility system makes them concise:

import { Search, X, Settings, Bell } from 'lucide-react'

{/* Basic icon button */}
function IconButton({ icon: Icon, label, onClick }: {
  icon: React.ComponentType<{ className?: string }>
  label: string
  onClick?: () => void
}) {
  return (
    <button
      onClick={onClick}
      aria-label={label}
      className="rounded-lg p-2 text-gray-500 hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-100 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500"
    >
      <Icon className="size-5" />
    </button>
  )
}

{/* Usage */}
<IconButton icon={Search} label="Open search" />
<IconButton icon={Settings} label="Open settings" />
<IconButton icon={Bell} label="View notifications" />

The aria-label prop is required for icon-only buttons — without it, screen readers have no way to announce the button's purpose. For a full guide on accessible icon implementation, see [Icon Accessibility in React](/blog/icon-accessibility-react-2026).

Navigation Icons with Tailwind CSS v4

Navigation is where icons appear most frequently. This pattern handles active states, hover states, and dark mode cleanly:

import { Home, BarChart, Users, Settings, Bell } from 'lucide-react'

const navItems = [
  { icon: Home, label: 'Dashboard', href: '/dashboard' },
  { icon: BarChart, label: 'Analytics', href: '/analytics' },
  { icon: Users, label: 'Users', href: '/users' },
  { icon: Settings, label: 'Settings', href: '/settings' },
]

function Sidebar({ currentPath }: { currentPath: string }) {
  return (
    <nav className="flex flex-col gap-1 p-4">
      {navItems.map(({ icon: Icon, label, href }) => {
        const active = currentPath === href
        return (
          
            key={href}
            href={href}
            className={`
              flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors
              ${active
                ? 'bg-violet-50 text-violet-700 dark:bg-violet-950 dark:text-violet-300'
                : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100'
              }
            `}
          >
            <Icon className="size-5 shrink-0" />
            {label}
          </a>
        )
      })}
    </nav>
  )
}

Performance with Tailwind CSS v4 and Icons

Tailwind v4's faster build engine reduces the overhead of processing icon-heavy component files. Full builds that previously took 800ms now take under 200ms in most projects. This does not affect runtime performance but meaningfully improves development experience.

For runtime bundle size, the same rules apply as in v3 — use named imports, avoid namespace imports, and use Server Components for static icons. See the complete guide to [icon bundle size optimization](/blog/icon-bundle-size-react-2026) and [icon subsetting for Next.js](/blog/icon-subsetting-nextjs-bundle-size-optimization) for detailed techniques.

The Best Icon Library for Tailwind CSS v4 Projects

For most Tailwind CSS v4 projects the decision is straightforward:

Use Heroicons if your project uses Tailwind UI components, you value the official Tailwind Labs integration, and 292 icons covers your needs. The design alignment between Heroicons and Tailwind is unmatched.

Use Lucide if you need more than 292 icons and want the best overall React developer experience. Lucide is the default in shadcn/ui which is the most popular component library for Tailwind CSS projects.

Use Tabler if Lucide's 1,500 icons are not enough and you need specialty icons for finance, medical, technical, or niche UI patterns.

Use Phosphor if your design needs multiple weight variants — thin icons on marketing pages, filled icons for active states — from a single library.

Use the [Best For You quiz](/best-for-you) to get a recommendation based on your specific framework, project type, and design requirements.


Frequently Asked Questions

← back to all posts