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 = falseDark 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.
| Prefix | CSS Equivalent | Example |
|---|---|---|
| hover: | :hover | hover:bg-blue-600 |
| focus: | :focus | focus:ring-2 focus:ring-blue-500 |
| active: | :active | active:scale-95 |
| group-hover: | parent:hover child | group-hover:text-blue-500 |
| disabled: | :disabled | disabled: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>