A Real-World Tailwind v4 Migration: What We Learned at JRV Systems
Considering a Tailwind v4 migration? We upgraded jrvsystems.app and saw a 30% CSS bundle reduction. Here’s what actually changes with the new Oxide engine.
Our Real-World Tailwind v4 Migration Test Case: jrvsystems.app
At JRV Systems, we believe in using the tools we recommend to our clients. When the alpha for Tailwind CSS v4 was released, we decided to perform a Tailwind v4 migration on our own company website, jrvsystems.app. This wasn't just a technical exercise; it was a practical test to understand the real-world benefits and challenges before applying it to client projects, from e-commerce platforms to complex SaaS dashboards.
Tailwind v4 introduces the new 'Oxide' engine, a complete rewrite in Rust promising significant performance improvements. But for developers and business owners in Malaysia, the key questions are practical: How much faster is it really? What breaks? And is the migration effort worth the payoff?
This article outlines our direct experience—the specific changes, the performance gains we measured, and our honest assessment of the process.
The Biggest Change: Config-as-CSS with @theme
The most significant architectural shift in Tailwind v4 is moving the configuration from a JavaScript file (tailwind.config.js) directly into your main CSS file. This is done using the @theme at-rule. For anyone who has worked with Tailwind before, this feels both strange and refreshingly simple.
Previously, your configuration for colours, fonts, and spacing looked like this in JavaScript:
// tailwind.config.js (v3)
module.exports = {
theme: {
extend: {
colors: {
'brand-blue': '#0052cc',
},
fontFamily: {
'sans': ['Inter', 'sans-serif'],
},
}
}
}
Now, the same configuration lives inside your CSS:
/* main.css (v4) */
@tailwind base;
@tailwind components;
@tailwind utilities;
@theme {
--color-brand-blue: #0052cc;
--font-family-sans: 'Inter', 'sans-serif';
}
This change has several practical benefits:
- Simplicity: No more context-switching between JS and CSS files to define your design system. Everything related to styling is in one place.
- Native CSS: It uses CSS Custom Properties (variables) under the hood. This makes the system feel more integrated with modern CSS standards.
- No Build Step for Config Changes: In many development setups, changing a CSS variable doesn't require a full server restart, unlike changing the
tailwind.config.jsfile. This speeds up the development feedback loop.
Performance Gains: The Oxide Engine and a 30% Bundle Reduction
Performance is not just a vanity metric; it directly impacts user experience, especially on mobile networks across Malaysia. The new Rust-based Oxide engine in Tailwind v4 is incredibly fast during development, but we were more interested in the final production output.
After completing the Tailwind v4 migration for jrvsystems.app and running our build process, we analyzed the final CSS bundle size. The result was a consistent reduction of approximately 30%. For our site, the production CSS file went from around 15.2 kB to 10.6 kB (gzipped).
While 5 kB might not sound like much, these small savings add up. For the complex web applications and e-commerce sites we build for clients, a 30% reduction in CSS can translate to a noticeably faster page load. This is because Oxide is more efficient at scanning files and generating only the exact CSS needed, with no unnecessary overhead.
Practical New Features: Container Queries and variant()
Beyond the headline changes, v4 introduces capabilities that solve common CSS challenges more elegantly. Two standouts for us were native container queries and the new variant() function.
Container Queries: Instead of relying only on the browser viewport size (md:, lg:), you can now change an element's style based on the size of its parent container. This is a huge improvement for creating truly reusable components.
@lg:text-lg: Changes text size when the container is larger than thelgbreakpoint.@container-name/lg:text-lg: Scopes the style to a specifically named container.
This is invaluable for dashboards and complex layouts where a component might appear in a wide main content area or a narrow sidebar and needs to adapt accordingly.
The variant() Function: This provides a cleaner way to group styles for a specific state, like hover or focus, without repeating the variant prefix.
/* Old way */
hover:bg-blue-500 hover:text-white
/* New way with variant() */
variant(hover, { & { @apply bg-blue-500 text-white; } })
While the syntax is still evolving, it points towards a more powerful and organized way of handling complex component states directly in your CSS.
The Migration Process: Was It Difficult?
Our experience with the Tailwind v4 migration was straightforward. Since v4 is designed to be largely backwards-compatible at the utility-class level, the core effort was focused on translating the tailwind.config.js file into the new @theme format in our CSS.
The main steps were:
- Update the
tailwindcsspackage to the latest v4 alpha version. - Remove the
tailwind.config.jsfile. - Create a new
tailwind.css(or equivalent) file if one didn't exist. - Add the
@tailwinddirectives and the new@themeblock. - Translate each key from the old JS config into its corresponding CSS Custom Property format (e.g.,
colors.brand-bluebecomes--color-brand-blue).
For a small-to-medium site like ours, the entire process took less than an hour. For larger projects with extensive custom configurations, this could take longer, but it's a one-time mechanical task.
Is the Tailwind v4 Migration Worth It for Your Project?
Based on our hands-on experience, the answer is yes, but with a note of caution. As of this writing, v4 is still in alpha. We wouldn't recommend it for a mission-critical client project launching tomorrow. However, for new projects or for teams looking to prepare for the future, starting with v4 or planning the migration is a smart move.
The performance benefits are real and measurable. The shift to config-as-CSS simplifies the developer workflow. For our team at JRV Systems, the faster build times and smaller asset sizes are compelling reasons to adopt it as our standard once it reaches a stable release. It allows us to deliver faster, more efficient websites and applications for our clients.