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.
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.
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.
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.
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.
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.
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:
<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>
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.
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>
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.
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>
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>
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>
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>
Pretty cool, isn’t it?
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>
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 tailwindcss@latest postcss@latest autoprefixer@latest
in your development environment. Then, in your tailwind.config.js
file, set the mode to jit
:
// tailwind.config.js module.exports = { mode: 'jit', purge: [ // ... ], // ... }
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.
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.
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.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. 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 and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowExplore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
Optimize search parameter handling in React and Next.js with nuqs for SEO-friendly, shareable URLs and a better user experience.