iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Content Detection / Purge

Tailwind purge / content scanning: how unused classes get removed from production CSS. (Now built into JIT.)

Tailwind — purge / content

EXAMPLE
<!-- ===== History ===== -->
<!--
Tailwind v2: 'purge' option ran PostCSS plugin at build time.
Tailwind v3: 'content' option drives JIT compilation.
Tailwind v4: 'content' auto-detected from your project structure.

The principle is the same: only ship CSS for classes you actually use.
-->

// tailwind.config.js (v3)
export default {
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx,vue,svelte}',
  ],
  theme: { extend: {} },
  plugins: [],
};

// Tailwind scans these files for class names and only emits those classes.

// ===== What gets scanned =====
// JIT looks for STRING literals that match class names.
// It's greedy: any string in your code can be a class.
// Tradeoff: false positives (CSS shipped you didn't use) vs simplicity.

// ===== Dynamic class names ===== =====
// JIT cannot see classes constructed dynamically:
const color = 'red';
<div class={\`bg-${color}-500\`} />   // BAD — JIT sees 'bg-' + '-500', not 'bg-red-500'

// Fix 1: spell out all options
const colorClasses = { red: 'bg-red-500', green: 'bg-green-500', blue: 'bg-blue-500' };
<div class={colorClasses[color]} />

// Fix 2: safelist
// tailwind.config.js
safelist: [
  'bg-red-500',
  'bg-green-500',
  { pattern: /bg-(red|green|blue)-(100|500|900)/ },
],

// ===== Production build =====
NODE_ENV=production npm run build
// Tailwind runs in JIT mode; output is minimised.

// ===== Inspecting output =====
// Build, then check dist/styles.css size.
// Typical sizes:
// - Small SPA: 10-30 KB minified
// - Large SPA: 50-100 KB minified
// - Pre-Tailwind / unpurged: 3+ MB

// ===== Include external component libraries =====
content: [
  './src/**/*.{ts,tsx}',
  './node_modules/@my-org/ui/**/*.js',   // libraries you depend on
],

// Otherwise their classes get purged in your build.

// ===== Common bugs =====
// - Classes 'mysteriously missing' from production build -> dynamic class names
// - Content paths missing a folder -> classes never appear
// - node_modules / dist included -> huge build files scanned
// - safelist drift -> classes survive long after the dynamic source is removed

// ===== Patterns =====
// - Wide content globs that match your source code
// - Spell out class names statically when possible
// - Use safelist with REGEX PATTERN for dynamic colour / size
// - Audit safelist when refactoring

// ===== Pitfalls =====
// - Including node_modules of unrelated libs -> slow build
// - Forgetting that mdx / html / pug / liquid files need explicit globs
// - Safelist with no expiry -> dead classes ship forever
// - Class names built from arrays of strings -> JIT sees individual strings

Why it matters

JIT / content scanning ships only the classes you use. Spell out class names statically, safelist dynamic ones, point content paths at every source file. Production builds typically land 10-100 KB minified — orders of magnitude smaller than the old purge era.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// content: ['./src/**/*.{html,js,jsx,ts,tsx}'] — anything outside is purged.
Try it Yourself »

Discussion

Loading…