Dominik Sobe ⚡ JavaScript developer and indie hacker blogging about designing and bootstrapping software startups on the interweb, technology, and more.

Exploring JIT mode in Tailwind CSS

7 min read 2087

Tailwind CSS is a collection of opinionated CSS utility classes that aims to make your life as a developer easier. With the new release of the Just-in-Time (JIT) compiler, Tailwind gets even more productive.

In this guide, we will take an in-depth look at Tailwind’s new JIT mode, why you should use it, practical new use cases, pitfalls, as well as how to install it. Let’s dive in.

What is JIT mode?

From Tailwind CSS version 2.1, the new Tailwind JIT compiler generates templates on demand, rather than creating everything in advance during the initial construction period. While this may not sound super interesting at first glance, it actually has major implications on your daily frontend work.

Why should I use JIT mode?

No need for custom CSS

Because styles are now generated on demand, you can create arbitrary values for almost any Tailwind class. This allows you to use Tailwind with custom values even when your design system does not support it.

We’ve all been in a situation in which we needed some ultra-specific value, and had to opt for creating a class or inline style that felt unnatural.

Super fast build times

According to Tailwind, compilation with the CLI initially took about 3–8s, and for Webpack applications up to 30–45s (Webpack struggles with huge CSS files). With the new JIT compiler, even the biggest projects can be compiled in 800ms, irrespective of what tool you use.

All variants work out of the box

Due to file size considerations, classes like active or disabled are normally not enabled by default. With JIT you never have to configure your variants again. Every variant is enabled by default.

Staging styles are the same as production styles

Because everything is generated on demand, there is technically no need to purge unused styles for production. This way, you can stop worrying about classes left unpurged in production and breaking your carefully crafted designs.

What can I do now that I previously could not?

Now let’s explore some exciting new things you can do with the JIT compiler. For this example, we will build a simple modal screen and apply the new JIT features step by step.

This is how the basic Tailwind code for our modal looks:

We made a custom demo for .
No really. Click here to check it out.

<div class="flex justify-center items-center w-screen h-screen bg-gray-900">
  <div class="w-60 h-60 bg-white relative rounded-lg flex flex-col justify-center items-center">
      <div class="font-medium text-xl">Thank you 🙏</div>
      <div class="text-sm text-gray-500">We appreciate your support.</div>
  </div>
</div>

Screenshot of a basic app with a dark background and a white square that says "thank you we appreciate your support"

First, give our parent div h-screen and w-screen components in order to center our modal. Next, center both parent and child divs with flex and apply some basic text styling to make it a bit more appealing to look at.

Now let’s see how we can use arbitrary values to solve a very common and annoying pre-JIT problem.

Arbitrary values

Let’s add a “close” button to our modal. To achieve this, we will create a div, give it a rounded-full component to make it a circle, and insert a check mark icon sourced from icons8.

Next we want to position our “close” button at the top right corner. Here’s where it gets interesting: Tailwind comes with a set of predefined values such as w-2 translating to 0.5rem (8px) or w-3 translating to 0.75rem (12px). But what if we want our design to be pixel perfect, and need that 10px width?

Previously, we had to either define a new variant, use inline styles, or create a custom CSS class. Neither of these options are perfect.

Luckily, for these situations, Tailwind’s JIT lets us use arbitrary values with the brackets [arbritary_value] syntax:

<div class="flex justify-center items-center w-screen h-screen bg-gray-900">
  <div class="w-60 h-60 bg-white relative rounded-lg flex flex-col justify-center items-center">
      <div class="absolute flex justify-center items-center rounded-full top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3]">
        <img src="https://img.icons8.com/ios-filled/100/ffffff/checkmark--v1.png"/>
      </div>
      <div class="font-medium text-xl">Thank you 🙏</div>
      <div class="text-sm text-gray-500">We appreciate your support.</div>
  </div>
</div>

Screenshot of the same app as before, with a blue check mark in the upper right corner of the white box.

As you can see, we worked quite extensively with our arbitrary values to style our button in the exact way we want: top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3].

In case you are thinking that overusing arbitrary values can quickly create a mess in our Tailwind code, then you are absolutely right! Nevertheless, if you use it carefully, it can be very powerful and make your development time much more productive.

Another very useful use case for arbitrary values is with custom gradients and grid layouts.

All variants are enabled by default

Because styles are generated dynamically, there is no need to specify which variants are available for each core plugin. That means we can now use variants without defining them in our tailwind.config.js beforehand.

This is the code that you would have needed to enable it pre–JIT:

// tailwind.config.js
module.exports = {
  mode: 'jit',
  variants: {
    extend: {
      backgroundColor: ['group-hover']
    }
  },
  purge: [
    // ...
  ],
  // ...
}

In this little example, we placed the group class on the parent div and group-hover on our close button. When we hover over the card, the background of the button will change to green and its size will scale to 110 percent:

<div class="flex justify-center items-center w-screen h-screen bg-gray-900">
  <div class="w-60 h-60 group bg-white relative rounded-lg flex flex-col justify-center items-center">
    <div class="absolute transform transition group-hover:bg-green-300 group-hover:scale-110 flex justify-center items-center rounded-full cursor-pointer top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3]">
      <img src="https://img.icons8.com/ios-filled/100/ffffff/checkmark--v1.png"/>
    </div>
    <div class="font-medium text-xl">Thank you 🙏</div>
    <div class="text-sm text-gray-500">We appreciate your support.</div>
  </div>
</div>

Gif of same app as before, but when the cursor moves over the white box, the blue checkmark turns green.

In addition to the group-* variants that style the element based on its parent state, we can now use the new peer-* variants to style an element based on the state of one of its previous siblings.

In the example below you can see that when we hover over the “close” button, the sub paragraph text fades to full black color and scales to 105 percent:

<div class="flex justify-center items-center w-screen h-screen bg-gray-900">
  <div class="w-60 h-60 group bg-white relative rounded-lg flex flex-col justify-center items-center">
    <div class="peer absolute transform transition group-hover:bg-green-300 group-hover:scale-110 flex justify-center items-center rounded-full cursor-pointer top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3]">
      <img src="https://img.icons8.com/ios-filled/100/ffffff/checkmark--v1.png"/>
    </div>
    <div class="font-medium text-xl">Thank you 🙏</div>
    <div class="peer-hover:text-black peer-hover:scale-105 transition text-sm text-gray-500">We appreciate your support.</div>
  </div>
</div>

Same gif as before, but when the mouse hovers over the green checkmark, the words "we appreciate your support" turn black and get larger.

Stack variants

The next new feature allows us to stack and combine our variants to target specific situations without having to write custom CSS. This can be especially helpful when working with responsive design breakpoint variants.

In the next example, we will stack the sm breakpoint (min-width: 640px) together with the hover variant so the background of our card changes at the mouseover only if device widths are bigger than 640px:

<div class="flex justify-center items-center w-screen h-screen bg-gray-900">
  <div class="w-60 h-60 group bg-white text-black sm:hover:bg-black sm:hover:text-white relative rounded-lg flex flex-col justify-center items-center">
    <div class="absolute transform transition group-hover:bg-green-300 group-hover:scale-110 flex justify-center items-center rounded-full cursor-pointer top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3]">
      <img src="https://img.icons8.com/ios-filled/100/ffffff/checkmark--v1.png"/>
    </div>
    <div class="font-medium text-xl">Thank you 🙏</div>
    <div class="text-sm text-gray-500">We appreciate your support.</div>
  </div>
</div>

Gif of same app as before, but when the mouse hovers over the white box it turns black. The view size is then changed to below 640px, and there is no color change on mouseover.

Pseudo-elements

With the new JIT mode, you can use specific Tailwind classes for styling almost every pseudo-element, like ::before, ::after, and ::first-letter.

That’s not all! We now have utilities for setting the content property, which can be super useful for the new before and after variants.

In the example below, we have added a before pseudo-element, made its content the pointing finger emoji, and set a left margin to keep it from sticking to our text. In addition, we have applied the first-letter variant to accentuate the “W” in our sub paragraph:

<div class="flex justify-center items-center w-screen h-screen bg-gray-900">
  <div class="w-60 h-60 group bg-white text-black sm:hover:bg-black sm:hover:text-white relative rounded-lg flex flex-col justify-center items-center">
    <div class="absolute transform transition group-hover:bg-green-300 group-hover:scale-110 flex justify-center items-center rounded-full cursor-pointer top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3]">
      <img src="https://img.icons8.com/ios-filled/100/ffffff/checkmark--v1.png"/>
    </div>
    <div class="font-medium text-xl before:content-['👉'] before:mr-3">Thank you 🙏</div>
    <div class="text-sm text-gray-500 first-letter:text-2xl">We appreciate your support.</div>
  </div>
</div>

Screenshot of same Tailwind app with the "W" in "We appreciate your support" appearing larger than the other letters.

Pretty cool, isn’t it?

Per-side border colors

Last but not least, let’s take a look at per-side border colors. This was one of the most requested features that could not be implemented before JIT mode due to file size considerations:

<div class="flex justify-center  items-center w-screen h-screen bg-gray-900">
  <div class="border-4 border-t-blue-500 border-r-pink-500 border-b-green-500 border-l-yellow-500 w-60 h-60 group bg-white text-black sm:hover:bg-black sm:hover:text-white   relative rounded-lg flex flex-col justify-center items-center">
      <div class="absolute transform transition  group-hover:bg-green-300 group-hover:scale-110 flex justify-center items-center rounded-full cursor-pointer top-[10px] right-[10px] w-[25px] h-[25px] p-[5px] bg-[#07B5D3]">
        <img src="https://img.icons8.com/ios-filled/100/ffffff/checkmark--v1.png"/>
      </div>
      <div class="font-medium text-xl before:content-['👉'] before:mr-3">Thank you 🙏</div>
      <div class="text-sm text-gray-500 first-letter:text-2xl">We appreciate your support.</div>
  </div>
</div>

Screenshot of same Tailwind app, this time with a different color on each border.

How to install

If you have never installed Tailwind before, I highly recommend you refer to their documentation. The installation process depends on your frontend framework and development setup.

However, nearly every installation approach consists of these two steps:

First, run npm install -D [email protected] [email protected] [email protected] in your development environment. Then, in your tailwind.config.js file, set the mode to jit:

// tailwind.config.js
module.exports = {
  mode: 'jit',
  purge: [
    // ...
  ],
  // ...
}

What are the drawbacks of JIT mode?

Tailwind is a great CSS framework to create and adhere to your design system. This allows your code to be more extendable and, most importantly, scaleable. However, by introducing arbitrary values to your code, you can imagine how it might turn into an undocumented mess, leaving you at risk for inconsistent design.

The good news is that if arbitrary values are used cautiously, this should not be a problem and instead make the code easier to read, because everything is written in Tailwind’s syntax.

Additionally, it is not possible anymore to nest @apply ... within @screen. This has its pros and cons, but ultimately leaves us with cleaner syntax. For example, take the following code:

.wrapper {
  @apply mt-4 flex justify-center items-center h-screen w-screen bg-gray-100;
  @screen md {
    @apply mt-20;
  }
  @screen lg {
    @apply flex-col;
  }
}

This code becomes cleaner without nesting @apply… within @screen:

.create-page-body {
  @apply mt-4 flex justify-center items-center h-screen w-screen bg-gray-100 md:mt-20 lg:flex-col;
}

Last but not least, JIT is still young and experimental, meaning that details may change as the Tailwind team continues to refine them. If you are building your own projects, I would suggest using this awesome new addition right away. However, if you are building a production-grade app, it might make sense to wait until Tailwind officially suggests it.

Conclusion

In this guide, we looked at Tailwind’s new Just-in-Time mode, why you should probably use it, practical new use cases, and potential pitfalls. In my opinion, you should definitely opt in for the new JIT mode if you can. It will not only make your projects run much faster, but equip you with new exciting Tailwind classes that make your code more streamlined, easier to read, and extendable.

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web apps — .

Dominik Sobe ⚡ JavaScript developer and indie hacker blogging about designing and bootstrapping software startups on the interweb, technology, and more.

Testing accessibility with Storybook

One big challenge when building a component library is prioritizing accessibility. Accessibility is usually seen as one of those “nice-to-have” features, and unfortunately, we’re...
Laura Carballo
4 min read

Leave a Reply