Controlling Tailwind CSS v3 Production Output with the content Config
Tailwind CSS v3 JIT only emits utilities found in files matched by content. Learn how to configure, verify, and recover from missing production styles.
16 Dec 2025, 08:15 UTC

The problem: utilities disappear without a warning
You deploy a Tailwind CSS v3 project and a button loses its background color, spacing, or responsive behavior in production, even though it looks correct locally. The cause is usually the content array in tailwind.config.js. Tailwind’s Just-In-Time (JIT) engine scans only the files matched by that array and generates CSS only for classes it detects. Classes in unmatched templates, classes built by string concatenation at runtime, or markup from third-party libraries can be absent from the production build with no build error or warning.
The useful takeaway is to treat content as an allowlist of source files, not an optional optimization. Any change to your directory structure, template format, or class-generation strategy requires a corresponding configuration check and a production CSS inspection.
Requirements before you configure
Before editing tailwind.config.js, answer three questions:
- Which directories contain HTML, JavaScript, TypeScript, Vue, Svelte, or other templates that reference Tailwind classes?
- Are any class names constructed dynamically, such as
bg-${color}-500, rather than written literally in source files? - Does your application include HTML you do not control, such as vendor widgets or email templates?
This note assumes Tailwind CSS v3, where JIT is enabled by default. Behavior in other major versions may differ, and you should verify the steps in your own build pipeline.
Smallest suitable design: the content array
A minimal configuration lists the glob patterns for every file type that can contain Tailwind classes:
module.exports = {
content: [
"./src/**/*.{html,js,jsx,ts,tsx,vue,svelte}",
"./public/index.html"
],
theme: {
extend: {}
},
plugins: []
};
The default extractor looks for class-like strings inside matched files. If a component moves from ./src/components to ./src/ui and the glob does not cover ./src/ui, its utilities are silently dropped from the generated CSS.
Trust and data boundaries
Not all class names are visible to the static scanner. The boundary between detected and undetected classes is where production failures usually start.
Dynamically generated classes
A class built with template literals, such as text-${size} or p-${space}, may not match the scanner’s heuristics because the full string does not exist statically in any source file. For these cases, use the safelist option to force specific classes or patterns into the final CSS:
safelist: [
{
pattern: /bg-(red|green|blue)-500/,
},
{
pattern: /text-(sm|base|lg)/,
}
];
Safelisting is appropriate for state-driven classes or third-party markup, but overuse negates JIT size benefits. Prefer literal utility classes in your own source files when possible.
Arbitrary values
Arbitrary values such as p-[17px] work without configuration changes, but they emit raw values into CSS and bypass theme design tokens. Use them sparingly; prefer extending theme.spacing so the design system remains consistent and the generated CSS stays predictable.
Non-standard templates
If you use a template format the default extractor does not parse, you can attach a custom extractor to a content entry. This moves parsing logic into your build configuration and should be covered by the same verification steps as the standard globs.
Operational checks
Run these checks from your project root with normal user permissions. You need read access to tailwind.config.js and execute access to your package manager or Tailwind CLI.
1. Build production CSS
npx tailwindcss -i ./src/input.css -o ./dist/output.css --minify
Replace ./src/input.css and ./dist/output.css with your actual input and output paths. The command writes a new file; there is no database or cluster state to roll back, but the generated CSS changes. Reverting tailwind.config.js and rebuilding restores the previous output.
2. Confirm expected utilities are present
grep -c "bg-blue-500" ./dist/output.css
Expected check: the count is greater than zero for utilities you know exist in matched templates. If the count is zero, either the file containing the class is outside the content globs, or the class name is dynamic and not safelisted.
3. Compare globs against actual files
find src -type f \( -name "*.html" -o -name "*.jsx" -o -name "*.tsx" \)
Compare this list against the directories covered by your content array. Any template directory missing from the configuration is a candidate for silent style loss in production.
Failure modes
| Failure mode | Cause | Detection |
|---|---|---|
| Missing utility in production | Template directory omitted from content | Production CSS lacks expected class; local build works because local files differ |
| Missing dynamic class | Class name assembled at runtime and not safelisted | Specific state or variant never renders its styles |
| Excessive CSS size | Overuse of arbitrary values or broad safelist patterns | Production CSS bundle larger than expected; grep shows many raw values |
| Third-party styles absent | Vendor HTML outside scanned paths | Embedded widget appears unstyled in production |
Conditions that would change the design
- Upgrading from Tailwind v2: replace
purgewithcontentand review class detection, because v2’s PurgeCSS behavior differs from JIT. - Introducing a non-standard template language: add a custom extractor to the relevant
contententry. - Generating class names at runtime: add targeted
safelistpatterns or generate a static class map instead of relying on runtime string concatenation. - Serving user-provided HTML: do not rely on scanning user input; safelist only the utilities you intend to support, and sanitize input separately.
Limitations
This guidance covers established Tailwind CSS v3 JIT behavior. It does not cover Tailwind v4 or unreleased changes. The example configuration and command paths are illustrative; they are not presented as tested output from your environment. Verify the generated CSS after every configuration change, and treat any class name that does not appear literally in scanned source files as a potential production failure.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.