This episode covers the risks of dynamic classes from user input, safe validation and safelist patterns, and how to avoid injection via class names. You also learn content scanning strategies for multi-tenant environments with proper isolation.

Dynamic content is a fact of life in real applications: colors from a database, sizes from user preferences, themes from configuration. But how you combine those values into a class attribute carries two risks: classes not generated by JIT, and more seriously — CSS injection through class names. Episode 11 covers both.
The first risk leaves UI unstyled; the second can be a security hole. Tailwind supports arbitrary values like p-[calc(100%-1rem)], and if that value comes from a user without validation, the user can inject dangerous arbitrary values into your stylesheet.
The most problematic pattern is assembling class names from string fragments:
const size = userInput; // nilai dari database atau form
const className = "p-" + size; // berbahayaTwo problems arise: JIT doesn't see the complete string p-4 in content, so the class isn't generated; and if size contains an arbitrary value, a user can inject arbitrary CSS rules through the arbitrary values feature. Avoid this pattern entirely.
Replace assembly with explicit mapping — every allowed value maps to a fully written class string:
const sizeMap = {
small: "p-2 text-sm",
medium: "p-4 text-base",
large: "p-6 text-lg",
};
const className = sizeMap[size] ?? sizeMap.medium;Class strings like "p-4 text-base" appear whole in content, so JIT generates them. Unknown values fall back to the sizeMap.medium default. This sizeMap[size] ?? sizeMap.medium pattern is safe, deterministic, and easy to test.
If mapping isn't feasible because the number of combinations is large but limited, use safelist:
safelist: [
"bg-red-500",
"bg-green-500",
"bg-blue-500",
"text-red-500",
"text-green-500",
"text-blue-500",
],Safelist forces those classes to always be generated. Keep its use limited — every entry adds CSS size regardless of use.
CSS injection happens when user values enter the class attribute and Tailwind accepts them as arbitrary values. Two layers of defense:
[, :, and ;.A simple validation example:
const allowedSizes = new Set(["small", "medium", "large"]);
const safeSize = allowedSizes.has(size) ? size : "medium";allowedSizes.has(size) ? size : "medium" guarantees only three values ever reach the markup. There's no path for a user-supplied arbitrary value to enter the class attribute.
Warning
Never put user data directly into a class attribute. If your application really must accept class names from outside, validate against an allowlist first, then render as a complete string — not as assembled fragments.
In a multi-tenant environment, a single Tailwind instance serves many tenants with different themes. The challenge: if all tenants are scanned together, all tenants' classes end up in one stylesheet — wasteful and risky for token leakage between tenants.
Recommended strategies:
An example of per-tenant builds on the CLI:
npx tailwindcss -i ./themes/tenant-a.css -o ./dist/tenant-a.css
npx tailwindcss -i ./themes/tenant-b.css -o ./dist/tenant-b.cssEach tenant gets a separate CSS file containing only the classes allowed for it — the result of truly isolated scanning.
In multi-tenant environments with SSR or API-driven flows, make sure the content list only includes files that actually write class names — templates and components, not runtime data. Scanning database contents adds build load with no benefit, because JIT only recognizes string literals in source, not values injected from outside.
Episode 11 closed the security and reliability gaps in dynamic content: explicit mapping replaces class name assembly, safelist handles limited sets, allowlist validation blocks injection, and per-tenant builds provide isolation in multi-tenant setups.
Key takeaways:
Next, in episode 12, we'll cover delivery & CDN — comparing build-time generation with the Play CDN, CSP and SRI security when using a CDN, and caching strategies and HTTP headers for static CSS files.