Tailwind Dynamic Classes: Why Your bg-red-500 Isn't Working
It works in dev, breaks in production, and there's no error message. Here's why Tailwind purges your dynamically constructed classes — and the fix.
The Problem
I had a component that accepted a color prop and constructed a Tailwind class dynamically at runtime, something like `bg-${color}-500`. It worked fine in development, but in production every dynamically built color was missing from the final CSS. The first time you encounter this, the behavior is genuinely disorienting — the same code that rendered correctly in dev produces unstyled elements in production, with no error message. The component renders, the class name is in the DOM, but the CSS rule for that class doesn't exist in the stylesheet.
The reason is that Tailwind's JIT compiler statically scans source files for complete class name strings at build time, and because the full class name never appeared as a literal string in the source, the compiler had no way to know it needed to keep it — so it got purged. Tailwind doesn't evaluate JavaScript to determine which classes might be constructed at runtime; it only looks for literal class name strings. This is a deliberate design choice (evaluating JavaScript at build time would be slow and unreliable), but it means any dynamically constructed class name will be missing from production CSS unless you make it visible to the compiler.
The reliable fix is to use an explicit map object that maps each possible prop value to its complete, literal class name string, rather than concatenating pieces at runtime. The alternative is the `safelist` option in tailwind.config.js. I prefer the map approach because it's more explicit about which values are supported, and TypeScript can validate the prop against the map at compile time rather than silently accepting an invalid value that would only fail visually in the browser. The safelist approach works, but it's easy to forget to update the safelist when you add a new color, and the failure mode is the same as the original bug — the class is missing in production with no error message.
