next.jstailwind-cssutilitytailwind-css-3

Tailwind removes dynamically generated classes when using utilities like cn and custom prefixes


I'm using Tailwind CSS in my project, and I have a utility function cn for conditional class names and a withPrefix plugin to dynamically generate class prefixes. Here's a snippet of my code:

import { withPrefix } from "../../plugins/with-prefix";

const TestPage = () => {
  return (
    <div
      className={cn("text-black", withPrefix("[&_p]:", "bg-red-200 absolute"))}
    >
      Test Page
      <p>asdf</p>
      <span>asdf</span>
      <p>asdflasdf</p>
    </div>
  );
};
export default TestPage;

The withPrefix function dynamically generates classes like [&_p]:bg-red-200 and [&_p]:absolute. However, Tailwind's purge mechanism (or JIT mode) removes these dynamically generated classes because it doesn't see them in the source files.

I've tried adding the classes to the safelist in the Tailwind configuration, but since these classes are dynamically generated, I can't know all the possible combinations in advance.

How can I make Tailwind retain these dynamically generated classes? Is there a better way to handle this use case? Any help would be greatly appreciated!


Solution

  • TailwindCSS parses static strings. It's not capable of interpreting the result of a function. As the documentation clearly states, it can only recognize class names that appear directly in the code.

    If you want to introduce shortcuts or aliases, you'll need to write a TailwindCSS plugin that can merge or transform certain syntax for Tailwind to understand.

    <div className={cn("text-black", "[&_p]:bg-red-200 [&_p]:absolute"))}>
      ...
    </div>
    

    However, it is a popular idea that can group class names using some syntax to avoid repetition of certain variants.

    Even if you write some plugin, it’s important to write syntax instead of a function (which only returns the correct result at runtime), so that TailwindCSS can interpret it when generating the compiled CSS. For example, something like this:

    <div className={cn("text-black [&_p]:(bg-red-200,absolute)"))}>
      ...
    </div>
    

    Note: This doesn't work by default, but there are TailwindCSS plugins available that offer similar syntax.