A Real-World Tailwind v4 Migration: What We Learned at JRV Systems
Practical insights from our Tailwind v4 migration at JRV Systems. We cover the new config-as-CSS, @theme syntax, container queries, and a 30% bundle size cut.
Tailwind CSS v4 is a significant update, centered around a new Rust-based engine called Oxide. While still in alpha, we decided to perform a Tailwind v4 migration on our own website, jrvsystems.app, to understand the real-world impact. The promised performance gains were compelling, and as a studio that builds for clients, testing new technology early is part of our process.
This article outlines the practical changes we encountered, the benefits we measured, and our thoughts on when you should consider migrating your own projects.
Why We Performed a Tailwind v4 Migration So Early
The primary driver was performance. The Oxide engine promised faster build times and, more importantly, smaller final CSS bundles. For any website, especially in a market like Malaysia where mobile data speeds can be inconsistent, a smaller footprint means a faster-loading page and a better user experience.
We wanted to quantify this. By migrating our own site, we could get concrete numbers instead of relying on benchmarks. It also allowed us to experience the developer workflow changes firsthand, which is crucial before recommending a technology to our clients in Seremban and beyond.
The Biggest Change: Configuration Moves into CSS
The most fundamental shift in Tailwind v4 is the removal of the tailwind.config.js file. All configuration now lives directly inside your main CSS file. This feels unusual at first but quickly starts to make sense. Your styling logic—both the utilities and their definitions—are now in one place.
Previously, you would define custom colours or fonts like this in JavaScript:
// tailwind.config.js (v3)
module.exports = {
theme: {
extend: {
colors: {
'brand-blue': '#0052cc',
},
},
},
}
In v4, you define this using the @theme directive in your CSS:
/* main.css (v4) */
@tailwind base;
@tailwind components;
@tailwind utilities;
@theme {
--color-brand-blue: #0052cc;
--font-family-sans: 'Inter', sans-serif;
}
This approach uses native CSS custom properties (variables). It simplifies the setup and removes the need to context-switch between JS and CSS files for theme modifications. For our team, this streamlined the process of tweaking design tokens.
Practical Changes in Day-to-Day Development
Beyond the configuration file, the development workflow has a few notable improvements that we found genuinely useful.
- One-off values: You no longer need to define a theme value for a single-use style. You can use arbitrary values, but v4 also supports CSS variables directly, like
w-[var(--sidebar-width)]. This is cleaner for dynamic layouts. - Native Container Queries: This is a major feature. Instead of using a plugin, you can now use
@variants for container queries. For example,@lg:text-lgwill apply thetext-lgutility when the parent container (marked with@container) has a width corresponding to thelgbreakpoint. This is a more intuitive and powerful way to build component-based layouts. - No More Prefixes: Variants like
hover:,focus:, anddark:are now composed without prefixes. This is a subtle but clean change. For example,dark:hover:opacity-80is now justdark:hover:opacity-80— wait, that's the same. The change is more about how it's implemented under the hood, leading to a more consistent engine, but the syntax for common variants remains familiar. - Zero-Configuration by Default: You can start using Tailwind v4 without any configuration at all. The engine intelligently finds your class names and generates only the CSS you need. You only add a
@themeblock when you need to customize the defaults.
Performance Gains: The Oxide Engine is Fast
This was the most anticipated part of our Tailwind v4 migration, and it delivered. The Oxide engine is noticeably faster during development, with near-instantaneous rebuilds when a class is changed. But the production build result was the key metric.
After migrating jrvsystems.app and running our build process, we measured the final, purged CSS bundle size. The result was a 30% reduction in file size.
This is a significant improvement. It comes from a more efficient CSS generation strategy and better default settings. For a simple marketing site, this might mean a few kilobytes saved. For the complex dashboards and SaaS applications we build, this could translate to a much larger reduction, directly impacting initial load times for users.
Is It Worth Migrating Now?
Based on our experience, the migration process was straightforward. The main task was converting our tailwind.config.js into @theme directives. The codemods provided by the Tailwind team can automate much of this.
So, should you migrate?
- For new projects: Absolutely. Starting with v4 (even in its alpha/beta stage) gives you a simpler setup and immediate performance benefits. The core features are stable enough for production use.
- For existing projects: It depends. If your project is large and you are facing slow build times or have a large CSS bundle, the performance gains from a Tailwind v4 migration are worth the effort. If your project is stable and performance is not a concern, you can wait for the official stable release to avoid any potential breaking changes during the alpha phase.
Overall, our experience was positive. Tailwind v4 simplifies the configuration, introduces powerful new features like native container queries, and delivers on its promise of better performance. It's a solid step forward for the framework.