Performance, Content Detection and Production Builds

Advanced
12 min

Performance, Content Detection and Production Builds

Tailwind's output is small because it contains only the utilities you use, which depends on the compiler finding every class in your source. v4 does this automatically, but automatic is not magical. In this lesson you will learn how content detection works, how to add or exclude sources, how to safelist classes the scanner cannot see, and how to produce a minified production build.

How v4 finds your classes

Tailwind v4 scans every file in your project for strings that look like utility classes and generates CSS for exactly those. There is no content array to configure. A few sensible rules keep the scan fast:

  • Files listed in .gitignore are skipped, so node_modules, build output and lock files are never scanned.
  • Binary files (images, fonts, video) and CSS files are ignored.
  • Scanning starts from the current working directory, or from the location you specify.

The compiler is written in Rust (the Oxide engine) and uses Lightning CSS for parsing, prefixing and minification, so full builds are several times faster than v3 and incremental rebuilds take microseconds.

Scanning is text-based and never executes code: bg-${color}-500 in a template literal produces no CSS, and a class stored in a database is invisible. Those cases need safelisting.

Adding and excluding sources

@source registers extra paths relative to the CSS file. The common case is a component library inside node_modules, which the default rules ignore:

css
@import "tailwindcss"; @source "../node_modules/@acme/ui"; /* scan a package for its utilities */ @source not "../src/fixtures"; /* exclude a folder (v4.1+) */

Two options on the import itself change the starting point:

css
@import "tailwindcss" source("../src"); /* scan only src instead of the whole project */ @import "tailwindcss" source(none); /* disable automatic detection entirely */

source(none) combined with explicit @source directives is the right setup for monorepos, where scanning every package would be slow and would pull in classes from unrelated apps.

Safelisting with @source inline

When a class must exist even though it never appears in a scanned file, declare it inline. Brace expansion generates whole families in one line:

css
@source inline("underline"); @source inline("{hover:,focus:,}bg-red-{50,{100..900..100},950}"); @source inline("{sm:,md:,lg:,}grid-cols-{1..4}");

The second line produces bg-red-50 through bg-red-950 with their hover: and focus: forms, and the third produces responsive grid columns. Keep safelists small: every entry is CSS shipped to every visitor whether used or not. The v3 safelist config key no longer exists.

Production builds

Each integration has its own way to minify:

| Setup | Production command | Minification | |---|---|---| | CLI | npx @tailwindcss/cli -i src/app.css -o dist/app.css --minify | The --minify flag | | Vite | vite build | Automatic | | Next.js / PostCSS | next build | Automatic via the framework |

Lightning CSS minifies and adds vendor prefixes, so autoprefixer and cssnano are unnecessary. A typical production stylesheet is well under 50 KB before compression; if yours is much larger, look for an over-broad @source, a large safelist or @theme static. The Play CDN has no production mode and must be replaced by a build step before launch.

Keeping the output small

  • Prefer utilities over @apply in large component classes; @apply duplicates declarations into every class that uses it.
  • Enable gzip or Brotli on the server; utility CSS compresses extremely well because of its repeated selectors.

Common mistakes

  • Adding @source "../node_modules" wholesale, which scans thousands of files and safelists nothing useful.
  • Assembling class names in JavaScript and then "fixing" it with a huge safelist instead of writing complete class names.
  • Forgetting --minify in a CLI build script while Vite users get it for free.
Quick Quiz
Question 1 of 2

Which files does Tailwind v4 skip when scanning for classes by default?

Key Takeaways

  • v4 detects classes automatically from all non-ignored text files; there is no content configuration.
  • @source "path" adds a directory, @source not "path" excludes one, and source(none) on the import disables auto-detection.
  • @source inline("…") safelists classes with brace expansion; keep it minimal.
  • Minify with --minify in the CLI; Vite and Next.js do it automatically through Lightning CSS.
  • Watch for over-broad sources, runtime-built class names and @theme static when the CSS grows.

Next lesson: Migrating from Tailwind v3 to v4 — run the upgrade tool and handle every renamed utility and behaviour change.

Performance, Content Detection and Production Builds - Tailwind CSS | CodeYourCraft | CodeYourCraft