How Tailwind CSS JIT Generates On-Demand Classes

The Tailwind CSS Just-In-Time (JIT) compiler fundamentally changed utility-first styling by replacing massive, pre-generated stylesheets with an on-demand compilation engine. Instead of compiling thousands of unused utility classes upfront and purging them later for production, the JIT engine dynamically generates the exact CSS required in real time based on your template files. This article explains the internal mechanics of the Tailwind CSS JIT compiler, detailing how it watches files, scans candidate strings, dynamically generates Abstract Syntax Trees (ASTs), and injects minimal CSS directly into your build pipeline.

1. File Watching and Content Configuration

The JIT compiler begins by observing the file paths defined in your tailwind.config.js file under the content array. These typically include templates, scripts, and component files such as .html, .jsx, .tsx, or .vue.

Whenever a file changes, the build tool (like Vite, Next.js, or PostCSS) alerts Tailwind's file-system watcher. Because Tailwind monitors these specific files, it only re-evaluates the code when a relevant file is modified, keeping build overhead minimal.

2. Fast Tokenization and String Scanning

Unlike standard CSS parsers or browser rendering engines, Tailwind does not attempt to parse the entire Document Object Model (DOM) or evaluate JavaScript execution. Doing so would make the build slow. Instead, it uses a high-performance, regular-expression-based token scanning algorithm.

The scanner reads the raw text of modified files and extracts potential utility strings. It breaks the text down into individual tokens by matching class-like strings using characters valid in CSS identifiers, including colons, dashes, brackets, and slashes. Any string matching the token pattern—such as px-4, hover:text-blue-500, or w-[120px]—is extracted as a potential "candidate."

3. Candidate Validation and Rule Matching

Once the scanner extracts candidate strings, it matches them against Tailwind’s internal registry of rules, variants, and design tokens:

  • Static Utilities: Standard utilities like flex, hidden, or p-4 are matched against Tailwind’s core utility map.
  • Variants: Prefixes like dark:, md:, or group-hover: are separated from the base class. The engine registers which media queries, pseudo-classes, or custom parent-selectors need to be wrapped around the rule.
  • Arbitrary Values: When a class contains brackets (e.g., top-[17px]), the parser decodes the arbitrary value, infers the CSS property type (e.g., length, color, or number), and maps it to the corresponding CSS rule dynamically.

If a candidate token does not match any valid rule, plugin, or arbitrary pattern, the compiler silently discards it without throwing errors.

4. PostCSS Abstract Syntax Tree (AST) Generation

For every valid candidate, the JIT compiler generates PostCSS Abstract Syntax Tree (AST) nodes in memory:

  1. Rule Construction: It builds a CSS declaration (e.g., padding-left: 1rem; padding-right: 1rem;).
  2. Variant Application: If variants exist, it wraps the declaration in appropriate CSS blocks, such as @media (min-width: 768px) or selector modifications like :hover.
  3. Deduplication: The compiler tracks previously compiled classes in memory so it never duplicates CSS declarations, even if a class appears across hundreds of components.

5. In-Memory CSS Injection

Instead of writing CSS directly to disk on every change, the JIT engine keeps the generated AST in memory. It serializes these AST nodes into pure CSS and passes them back to the bundler through the standard PostCSS pipeline.

During development, the generated styles are served directly via Hot Module Replacement (HMR), providing instantaneous browser updates. In production builds, the compiler runs a single pass over all specified content files, generating a hyper-optimized stylesheet that contains only the CSS classes explicitly used in the project.