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

JIT Engine

Tailwind JIT mode: on-demand compilation, arbitrary values, and the perf wins. (Standard since Tailwind 3.)

Tailwind — JIT

EXAMPLE
<!-- ===== What JIT is ===== -->
<!--
JIT (Just-In-Time) compiles ONLY the classes you actually use, AS you use them.
Before JIT, Tailwind generated all possible classes (huge dev builds, PurgeCSS at end).
Since Tailwind 3, JIT is the only mode. Old 'mode: jit' option is obsolete.
-->

<!-- ===== What you get ===== -->
<!--
1. Arbitrary values:
     w-[37px], bg-[#1f2937], grid-cols-[200px_1fr], top-[calc(100%-4px)]
2. Stacked variants:
     md:hover:focus:bg-blue-500
3. Tiny dev bundles (only what is used)
4. No build-time purge step
5. New classes appear instantly
-->

<div class="grid grid-cols-[200px_1fr_auto] gap-[clamp(1rem,2vw,2rem)]">
  <aside class="sticky top-[64px]">...</aside>
  <main>...</main>
  <aside>...</aside>
</div>

<button class="bg-[#2d6cdf] hover:bg-[#1e57c0]">Save</button>

<!-- ===== Content paths matter ===== -->
<!--
JIT scans your files for class names. Misconfigure paths -> classes do not appear.
-->
// tailwind.config.js
export default {
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx,vue,svelte}',
    './node_modules/@my-org/ui/**/*.js',   // include design system
  ],
};

<!-- ===== Limits of arbitrary values ===== -->
<!--
- Class names must be statically discoverable. Dynamic strings are not detected:
    BAD:  <div class="bg-[#${color}]" />     // bg-[#] is what is scanned
    GOOD: <div class={{ ['#abc123']: true }[color]} />   // explicit branches
    GOOD: <div class="bg-[var(--brand)]" style={{ '--brand': color }} />

- Spaces in arbitrary values must be underscores:
    grid-cols-[1fr_auto]    NOT grid-cols-[1fr auto]

- Long arbitrary values reduce readability; prefer tokens in theme.extend.
-->

<!-- ===== Safelist for dynamic classes ===== -->
<!--
If you build class names at runtime (e.g. from API data), JIT will not see them.
Safelist them explicitly:
-->
// tailwind.config.js
export default {
  content: [...],
  safelist: [
    'bg-red-500',
    'bg-green-500',
    { pattern: /bg-(red|green|blue)-(100|500|900)/ },
  ],
};

<!-- ===== Common patterns ===== -->

<!-- Custom container sizes -->
<div class="w-[480px] sm:w-[640px] lg:w-[800px]">...</div>

<!-- Specific shadow / gradient -->
<div class="shadow-[0_4px_12px_rgba(0,0,0,0.08)] bg-gradient-to-r from-[#2d6cdf] to-[#19357a]">...</div>

<!-- One-off animation -->
<div class="animate-[bounce_2s_ease-in-out_infinite]">...</div>

<!-- ===== When to use config tokens instead ===== -->
<!--
If you use the same arbitrary value 3+ times, promote it to theme.extend:
-->
// tailwind.config.js
extend: {
  colors: { brand: '#2d6cdf' },
  spacing: { 'sidebar': '280px' },
  fontFamily: { display: ['Inter Display', 'sans-serif'] },
},

<!-- Now: bg-brand, w-sidebar, font-display — readable + consistent. -->

<!-- ===== Patterns to internalise ===== -->
<!-- - Content paths cover ALL files using classes -->
<!-- - Use arbitrary values sparingly; extend the theme for repeated patterns -->
<!-- - Safelist dynamic classes -->
<!-- - cn() helper to compose conditional classes -->

<!-- ===== Pitfalls ===== -->
<!-- - Dynamic class names that JIT cannot see -->
<!-- - Class strings inside conditionals JIT cannot statically detect -->
<!-- - Long arbitrary values littering markup; promote to tokens -->
<!-- - Missing safelist for classes built from API data -->

Why it matters

JIT is just how Tailwind works now: on-demand compilation with arbitrary values + tiny bundles. Get the content paths right, safelist dynamic classes, and promote repeated arbitrary values into theme.extend. The discipline keeps utility-first sustainable.

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

Example

Example
# The JIT engine generates only the classes you actually use.
# Result: tiny CSS, arbitrary values work.
Try it Yourself »

Discussion

Loading…