Efficiently Manage Dynamic Tailwind CSS with Safelist & Theming

Karangiya VrajKarangiya Vraj
5 min read

Introduction

Tailwind CSS is a utility-first CSS framework that’s loved for its speed, scalability, and productivity. But when building enterprise-level apps that integrate with a CMS like Sitecore and support multiple brands or single brand, developers often run into a major limitation: dynamic class names don’t work as expected. Solving this issue can significantly enhance the efficiency and maintainability of multi-brand environments.

This blog is a detailed guide that walks you through:

  • What Tailwind CSS is and how its Just-In-Time (JIT) engine works

  • Why dynamic classes fail in Tailwind

  • How the safelist feature helps

  • How we integrated this into a single or multi-sites project

Table of contents

  1. What is Tailwind CSS?

  2. How Tailwind's JIT Engine Works

  3. What Are Dynamic Classes?

  4. What is the safelist in Tailwind?

  5. Real-World Problem: Dynamic Styles in Multi-Brand Apps

  6. Simple Real-World Example (Multi-Brand Card)

  7. Summary

1. What is Tailwind CSS?

Tailwind CSS is a utility-first CSS framework that allows you to build designs directly in your markup using small, composable utility classes.

Example:

<div class="bg-blue-500 text-white p-4 rounded">Submit</div>

Instead of writing custom stylesheets, Tailwind encourages using these pre-defined utility classes. It compiles only the classes found in your source code, keeping your final CSS bundle small and fast.


2. How Tailwind's JIT Engine Works

Tailwind uses a Just-In-Time (JIT) engine that scans your project files at build time and compiles only the CSS classes that it can statically detect.

✅ This works:

<div class="text-sm bg-red-500 h-12">Static class example</div>

❌ This fails:

const color = 'red';
return <div className={`bg-${color}-500`}>Dynamic class example</div>;

Because Tailwind doesn’t evaluate JavaScript logic, it never sees bg-red-500 during its scan—so it never generates that CSS.


3. What Are Dynamic Classes?

Dynamic classes are class names built using variables or expressions at runtime.

Example:

const height = '40px';
return <div className={`h-[${height}]`}>Dynamic class example</div>;

Even though height becomes 40px, Tailwind doesn't know that ahead of time. It needs exact strings like h-[40px] in your code to include them in the final CSS.

⚠️ This problem affects both development and production.


4. What is the safelist in Tailwind?

To handle this limitation, Tailwind introduced a config option called safelist.

📌 Definition:

The safelist array in your tailwind.config.js allows you to specify classes that should always be included in your compiled CSS—even if Tailwind doesn't detect them in your code.

Example:

// tailwind.config.js
module.exports = {
  safelist: [
    'bg-red-500',
    'h-[40px]',
    'skew-x-[10deg]',
  ],
};

5. Real-World Problem: Dynamic Styles in Multi-Brand Apps

Our project is an enterprise-grade web app powered by Sitecore CMS. It supports multiple brands such as:

  • Site-A

  • Site-B

  • Site-C

  • Site-D

Each brand provides its own tokens for spacing, colors, shadows, etc., via JSON. These token values are not known until runtime and are injected into components dynamically.

Token file.

{
  "Site-A": { "bg": "#fefefe", "height": "100px" },
  "Site-B": { "bg": "#efefef", "height": "120px" },
  "Site-C": { "bg": "#dedede", "height": "90px" },
  "Site-D": { "bg": "#f2f2f2", "height": "70px" }
}

You try to render a component like this:

<div className={`bg-[${tokens.bg}] h-[${tokens.height}]`}>Card</div>

❌ Tailwind JIT ignores these because it can’t resolve their values statically.


6. Simple Real-World Example (Multi-Brand Card)

Let’s understand this with a simple example: You’re creating a Card component that changes background color and height based on a brand.

❌ Problematic Code

const brand = 'Site-A';
const styles = {
  Site-A: { bg: '#fefefe', height: '100px' },
  Site-B: { bg: '#efefef', height: '120px' },
  Site-C: { bg: '#dedede', height: '90px' },
  Site-D: { bg: '#f2f2f2', height: '70px' }
};

return (
  <div className={`bg-[${styles[brand].bg}] h-[${styles[brand].height}]`}>
    Branded Card
  </div>
);

This will fail because Tailwind can't detect dynamic strings.

✅ Solution with Safelist

Let’s fix this using safelist and a structured approach.

Step 1: Define brand styles

const brandStyles = {
  Site-A: { bg: '#fefefe', height: '100px' },
  Site-B: { bg: '#efefef', height: '120px' },
  Site-C: { bg: '#dedede', height: '90px' },
  Site-D: { bg: '#f2f2f2', height: '70px' }
};

Step 2: Generate the Safelist Programmatically

const safelist = Object?.values(brandStyles)?.flatMap(({ bg, height }) => [
  `bg-[${bg}]`,
  `h-[${height}]`
]);

This gives you:

[
  'bg-[#fefefe]', 'h-[100px]',
  'bg-[#efefef]', 'h-[120px]',
  'bg-[#dedede]', 'h-[90px]',
  'bg-[#f2f2f2]', 'h-[70px]'
]

Step 3: Add to tailwind.config.js

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
  safelist: safelist,
};

Step 4: Use in component

const brand = 'Site-A';
const styles = brandStyles[brand];

return (
  <div className={`bg-[${styles.bg}] h-[${styles.height}]`}>
    Branded Card
  </div>
);


7. Summary

ConceptSummary
TailwindUtility-first, JIT-based CSS framework
JIT EngineRequires static class detection
Dynamic ClassesBuilt using JS variables—ignored by JIT
SafelistExplicitly tells Tailwind which classes to keep
Sitecore IntegrationToken values per brand at runtime, injected into UI

Final Thoughts

Tailwind CSS is a powerful framework, but like any tool, it has constraints. In CMS-driven, multi-brand enterprise applications like those built with Sitecore, dynamic styles can become a major bottleneck if not handled correctly.

By combining:

  • Token extraction from CMS

  • Safelisting dynamic Tailwind classes

You can unlock a highly scalable and maintainable architecture while keeping the benefits of Tailwind’s utility-first approach.


💬 Join the Conversation

Have you faced similar challenges working with Tailwind in multi-brand or CMS-driven environments like Sitecore?

Let’s exchange ideas, lessons, and solutions—connect with me on LinkedIn or drop a comment below.

1
Subscribe to my newsletter

Read articles from Karangiya Vraj directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Karangiya Vraj
Karangiya Vraj

Passionate Frontend Developer with 2.5 years of experience specializing in React.js and Next.js. Skilled in building responsive, user-friendly interfaces using Tailwind CSS. Enthusiastic about creating dynamic and engaging web applications.