Dynamic Styling

Tailwind classes are static strings, but your UI is dynamic. Learn how to conditionally apply classes based on state, screen size, and user interaction.

Conditional Classes

The most common dynamic pattern: toggling classes based on component state (like isActive, isOpen, or isLoading).

Conditional Classes with cn()
import { cn } from "@/lib/utils";

function Tab({ isActive, children }) {
  return (
    <button className={cn(
      // Base styles (always applied)
      "px-4 py-2 rounded-lg font-semibold transition-all",
      // Conditional styles
      isActive
        ? "bg-indigo-500 text-white shadow-md"
        : "bg-transparent text-neutral-500 hover:text-neutral-900"
    )}>
      {children}
    </button>
  );
}
Live Demo: Toggle Active State
⬜ Inactive
isActive = false

Dark Mode

Tailwind's dark: prefix lets you define alternate styles that activate when dark mode is enabled. This app uses dark mode right now — try toggling the theme in the header!

Dark Mode Prefix
<div className="
  bg-white         // Light mode: white background
  dark:bg-neutral-900  // Dark mode: dark background

  text-neutral-900       // Light: dark text
  dark:text-neutral-100  // Dark: light text

  border-neutral-200       // Light: subtle border
  dark:border-neutral-800  // Dark: dark border
">
  This card adapts to your theme!
</div>
How it works: By default, Tailwind v4 uses the class strategy — dark mode activates when a .dark class is present on the <html> element. Libraries like next-themes handle this toggle automatically.

State Variants

Tailwind provides prefixes for every interactive CSS pseudo-class. No need for separate :hover rules in a stylesheet.

PrefixCSS EquivalentExample
hover::hoverhover:bg-blue-600
focus::focusfocus:ring-2 focus:ring-blue-500
active::activeactive:scale-95
group-hover:parent:hover childgroup-hover:text-blue-500
disabled::disableddisabled:opacity-50
Try these buttons:

Responsive Prefixes

Every utility class can be made responsive by prefixing it with a breakpoint. Tailwind is mobile-first — unprefixed classes apply everywhere, and prefixed ones kick in at that width and above.

Responsive Layout
<div className="
  grid grid-cols-1   // Mobile: 1 column
  sm:grid-cols-2     // ≥640px: 2 columns
  lg:grid-cols-3     // ≥1024px: 3 columns
  gap-4
">
  <Card />
  <Card />
  <Card />
</div>

// Text that scales with viewport:
<h1 className="
  text-2xl          // Mobile: smaller
  md:text-4xl       // Tablet: medium
  xl:text-6xl       // Desktop: large
">
  Responsive Heading
</h1>