I see Tailwind being used by a lot of people. I totally get why it’s so popular. It looks great, the design choices are sensible, and you have copy-pastable code that reliably renders in the same way.
It nets you results quickly which feels nice and rewarding. I think Tailwind is a perfect framework for two kinds of situations:
- people who don’t want to learn CSS
- people who want a standardized look and want to copy paste from an external library (i.e. Tailwind UI)
I mostly wanted to shut up about Tailwind, and I hoped that people would realize it should be avoided after a while. But that doesn’t seem to be happening. So I think I have to fight the fight and talk about why you should not use Tailwind.
I have had a lot of thoughts about Tailwind, and most of these thoughts come from a place of being the code maintainer of a large scale web application codebase. So mind you, when I am talking about regret in the title, I am talking about a big web app context where your code is going to be maintained for years to come.
I am not talking about your Sunday side project. I am talking about real, big work® projects here. I am talking about maintaining something that has a lot of complexity that many people will work on for years to come.
You might be tasked with choosing a CSS architecture for that. And you might have heard about this framework called Tailwind. It’s popular right now and it seems to be the 2020 choice.
You look at the website and you see this nice framework that promises you that things are going to be cool in Tailwind land. And you might be thinking: cool, let’s try that.
I urge you to reconsider your options. I would advise you to not use it.
I really don’t know why Tailwind got so popular over the last year. I think it’s because it does a lot of things right. Surely if the authors can make $1.8M in preorders for Tailwind UI, something must be up. But at a fundamental level, I think its ideas are wrong and lead to problems, and I am going to explain why.
The main problem is that the kind of code you are creating with Tailwind, especially on large scale applications, is the kind of code that you’re going to have to swap out in a few years. Why? Because it’s unmaintainable at best. I will explain below.
An API on top of an API
Tailwind is a framework that heavily relies on utility classes. Utility classes are classes that typically do one single thing e.g.
.flex makes a
display: flex. Utility classes create an API on top of what is already a declarative API (CSS itself).
This can lead to problems when
- the original API (CSS itself) might change in the process, so now you have a mapping problem
- the mapping itself is not that great, and has bugs on its own
To dive into situation 1, let’s think about how maybe CSS grid will have an evolution in its syntax. Now you don’t just have to update your code, but also the in-between framework (Tailwind).
To dive into an example of situation 2: I was on a project a while ago where somebody mapped Flexbox to utility classes wrongly (i.e. the mapping contained obvious mistakes) and that code had made into production, was versioned, and we couldn’t change it. I thought that was very painful.
Tailwind’s coding style creates its own problems, which you then have to solve
There’s also a deeper problem: because you are essentially recreating the CSS API with utility classes, you end up with a huge file. The current Tailwind utilities.css file is 1.8 megabyte and contains 80 000 lines of code.
Tailwind proponents will tell you that you can remove unused CSS with Tailwind’s tooling. And this is exactly the problem that I want to talk about: now you have to solve a problem that’s inherent to the framework, adding complexity to your project.
In the Routify and Svelte Discord channels I routinely see people struggling to get the toolchain around Tailwind working. So many people struggling with a toolchain that is totally unnecessary is a real problem. Removing code that shouldn’t be there in the first place should not be necessary.
Sidenote: I’m not against utility classes
Surely it feels nice to be able to quickly write flexbox things in a declarative way, and it has it’s uses. I’ve happily wrote declarative flexbox code in the past to quickly set up layouts. I am not against utility classes at all. It’s quite handy to set up layouts quickly with utility classes. I can also totally relate to the idea of designing in the browser with a set of utility classes.
When I do web app layouts we typically have a lot of different panes and panels going on in your app with varied logic about how they should resize. In a prototyping or an early app phase it’s super handy to not have to write a specific (BEM) component for everything and just build things out with utility classes.
But you have to be careful with utility classes. Because what you’re essentially doing is building a complication on top of CSS itself.
When you use a utility classes, for example declaring a flex utility on a div, you are doing it in HTML instead of declaring it in CSS this can quickly lead to unmaintainable code.
I use utility classes in one-off situations. Tailwind style code uses utility classes for everything. Tailwind lovers will now come back to me with arguments about
@apply but this is just throwing more complexity on something that is already complex.
Adding complexity to complexity
Tailwind’s HTML code is fine for very simple projects (which will now have the drag of a whole toolchain to purge unused selectors but OK).
But let’s just imagine you have a bit of a bigger project, with a basic app layout that is working, with a few panels and a few buttons.
You now have to update the code for the next requirement – a bit of responsiveness. The UI has to change according to a breakpoint. So you add some Tailwind breakpoint classes to the thing you are making.
You realize your specific situation requires a very specific breakpoint. So you change your Tailwind config to add this breakpoint. You kind of realize it would be easier to just write the breakpoint directly in CSS, but you want to do things the Tailwind way.
If you would do things the regular CSS way, your media queries list basically lives in two worlds: the tailwind config and a local CSS file. And now you’ve lost your breakpoint structure. So you have to adhere to the Tailwind style.
And then let’s say you have a new situation: dark mode has to be supported. So suddenly your colors all have to change according to the dark mode logic. So now you add a bunch of classes to whatever you are creating again… you look at your button code and you see that the color of the button is directly in the HTML (
gray-100). And the opposed color is actually called
gray-900 in your design system. So now you add a class to the button for the dark situations. And you now realize that there are 7 variants of your button in your codebase and they all have different class combinations.
Now, with the previous responsive example, and this dark thing piled on top of it, you have a sorry mess.
And then you realize that some aspects of what you’re doing would be way easier to handle directly in (S)CSS, using contextual media queries and proper use of the cascade.
Maybe your web app is never going to see the complexity I am talking about. Maybe your apps are not responsive or they don’t support dark mode. But most of my work supports some form of skinnability, is as responsive as possible and needs to be maintained over several yerars.
Putting values on the same class level in HTML is basically destroying the cascade for you. So don’t do it.
Complexity overload and no proper way to manage it
The problem with Tailwind is that you end up with an overload of classes in your HTML and no way to manage these.
Tailwind’s creator says it’s not a problem because your code can essentially lives in deep presentational components and it can be abstracted away. But this kind of coding strategy leads to its own problems (e.g. prop drilling)
I’m especially wary of this situation where you have one button that has 6 HTML classes, and then another button that has 5 HTML classes, and another one that has 7 HTML classes. And they’re all different combinations. Now you have to do some serious digging through the codebase to try and bring everything in line, and you’re going to be comparing which of the three buttons have class overlap. Nobody is waiting for that task. And frankly, it’s probably just not going to happen.
It’s code rot that’s going to happen as soon as you have a few people working on a project with moderate complexity.
I fear for a Tailwind future
I fear for the general codebases of web apps when too many people decide that Tailwind is the way to go. Now, on the other hand, this means I’ll have an infinite stream of work to refactor bad codebases to better ones. So I guess I’m not complaining.
Just do yourself a favor and stop using Tailwind.
I do not agree. Tailwind establishes clear and concise design language among developers, designers and other parties. It also cuts down a lot of CSS that repeats over and over 8n project.
I work on big ass project without tailwind and it’s a pain.
To each his own man, no need to shout out that one tool or other is bad. You don’t like it, don’t use it. Let people decide what suits them best.
“If you don’t agree with someone, keep it to yourself”? Wow, this is some really bad reaction.
For people to decide what technologies suit them best, they need to know their pros and cons, which is exactly what this article is presenting regarding Tailwind. It might deter people looking for a styling solution from using Tailwind because they realise it’s actually not the ideal solution, contrary to what other articles might say.
Besides, constructive criticism like expressed here is what allow progress and improvement of our tools. Please instead of trying to shut it up, be thankful that people take the time to write stuff like that, and maybe reply to it with precise counter arguments?
I have to do some digging to find your article. Most of the reviews out there seem too good to be true. I understand that any framework SHOULD have reasonable cons, ie when I shouldn’t use it. It’s like the OOP frenzy as if procedural programming is spaghetti coding, no exception.
Thank you for being a level-headed skeptic. At least now, even if I am to use Tailwind CSS, I understand what’s insured and what’s not. It’s funny when I searched ‘how to migrate from tailwind css’ it served the opposite. It feels like converting to Islam, no way out, just in.XD
I completely agree. Writing all of these classes all over the place. When it comes time (and it will) someone is going to have a hard time rewriting all of this CSS. There’s a reason we don’t write inline styles ?
Thank you very much for this article! It does indeed address a number of points that I had not thought of in this consequence.
I’m currently facing the task of setting up a larger project for a client and have been thinking about integrating Tailwind CSS into my workflow. After reading the documentation twice and watching all the official tutorial videos, I was really tempted to jump on this bandwagon. Especially the code reduction with PurgeCSS was very tempting. Besides, some users claim that they have tripled their coding speed ;-)
But…since I am someone who has always tried to follow best practices, web and coding standards, I was deterred from the beginning by the irritating HTML code that comes with the use of Tailwind CSS. Now, your arguments are very valid and I don’t have to think about it much longer.
I am very grateful for this article, which opened my eyes and brought me back to the basics of good code.Thank you for stopping me from making a potentially serious mistake.
Interesting article. I think it’s important to abstract Tailwind CSS and Tailwind UI, two different things entirely. This post sounds like you’ve never actually used Tailwind in a project. As someone who has used Tailwind on two projects now (one big, one small), you don’t have to use Tailwind’s HTML code (and in fact I’d argue you wouldn’t want to and is probably one reason why they spun up Tailwind UI). Additionally, even if W3C deprecates CSS referenced by Tailwind, browsers rarely completely remove support for it. So, I’m having a hard time understanding exactly what your issues are with Tailwind other than you just don’t like it.
I feel like the creator put pretty solid counter-arguments out years ago, that are a little more detailed and less … dismissive.
Tailwind has many things going for it (especially in the idea of being a design system implementation), but I think it’s riding the hype train quite hard right now so I appreciate your article with critical thoughts about it!
I have just started learning Tailwindscs few days back and before jumping into it I started analysis of it’s real benifits and some Pros and Cons. At the initial level it seems very promising to me but after reading your articles I am not able to take a decision that how utility-first CSS framework can be a big trouble.
After reading your article I have found following few scenarios where Tailwindcss is not fitting well as per your opinion.
1) When There is a large scale project or a complex project
2) When there is a time for major HTML/CSS code change/rewrite in the project.
3) when there is a change in CSS API.
Also if possible, can you please provide some simple code examples to elaborate your thoughts in details on following points. That will be helpful to understand the drawbacks of this framework properly.
1) “Tailwind’s coding style creates its own problems, which you then have to solve”
2) “I’m especially wary of this situation where you have one button that has 6 HTML classes, and then another button that has 5 HTML classes, and another one that has 7 HTML classes. And they’re all different combinations. Now you have to do some serious digging through the codebase to try and bring everything in line, and you’re going to be comparing which of the three buttons have class overlap.”
your further guidance will be very helpfull for the developers like me.
While personally I clearly prefer writing my own CSS (here is my reasoning: https://www.gsarigiannidis.gr/escaping-the-css-framework/), I would say that whether using a framework is benefitial or not, depends on the specific use-case.
For example, if you are a developer with no design skills and no specific design at hand, then using a framework like material UI, Bootstrap is not only helpful, but I would dare say, essential.
If for whatever reason you want to write the less CSS possible, either because you hate it and/or because you are not good at it, then a utility framework like Tailwind might be your thing.
If you work on a big project along with other people, again, a framework might be helpful to keep the code consistent. On that case, though, I would support even more the use of vanilla CSS with some specific guidelines when it comes to naming and structure, as a framework – any framework – might become obsolete after some time.
If, on the other hand, you have to convert a design from a PSD, XD or whatever to HTML, then, depending on the design’s complexity, a framework might even turn out to be an obstacle rather than a help.
Also, I am a fan of avoiding frameworks when I have to build a plugin or a theme meant to be used by anyone and not for a specific client. On those cases, I believe that the less dependencies, the better (that applies to the use of vanilla JS instead of jQuery as well).
Reading the article from the comments above: “Adam Wathan – CSS Utility Classes and Separation of Concerns”.
I agree with the tailwind author about the power of utility classes and one of his final statements: ‘You should still use component classes … and extract repeating utility patterns as they emerge”
But let’s be honest. If Adam Wathan, tailwind author, states that you should extract repeating utility patterns to separate css files as a single component class. Then why should you even use tailwind?
– Using “@apply block;” in a css file over “display: block;” is just silly!
– Those uniform design tokens, like: font sizes, line-heights, spacings, gaps, etc… Do you realy need tailwind for those extracted repeating utility patterns? No, there are better options.
And lets not forget that making a component re-usable, usually mains that it’s appearance is (less / more) controlled by a (grand-) parent component, or by a modifier class. These issues are resolved in css, not html.
Like Johan Ronsse says: Try to stay away from css frameworks, aim for quality in your work!
@Johan Ronsse – Tailwind and others alike are likely to stay, because they answer a need. Faster development and more profit!
kinda messed up but i needed to find some type of validation as to why i refused to invest any of my time learning tailwind. my only reason so far was needing to avoid anything that looks like bootstrap and it’s use of utility classes. my ocd gets kicked into overdrive looking at all of the polluted markup and I’m now learning laravel and purposefully avoiding all of these tailwind landmines you encounter in the community based help. to me nothing beats having a clean, bem based markup.
I think I agree because we’re both designers. Tailwind reminds me of Agile. Both are by-devs/for-devs Trojan horses that allow developers to circumvent friction from designers and other stakeholders who exist for a reason.
I’m one of the people who want a standardized look and want to copy-paste from an external library (i.e. Tailwind UI).
Indeed I can copy the code from Tailwind UI and paste it into my template. It works great here.
However, when I take a look at the css file, it is 2.9MB. I know I can use a build tool. But I’m using docker-compose. So it adds complexity to my project. I have to add a new node image to include post CSS, node / npm, tailwind. I don’t know how to minify the css file to less than 20KB yet.
Also, the html code looks overwhelming. There are many classes everywhere. I can handle it now given that I just play with it for 3 days. I am not sure if I can handle it in the long run.
I hope it can work like alpine.js / vue.js which I can work with CDN with a relatively small size, instead of a gaint dinosaur. The CDN version is probably not suitable for production
Anybody who is using Tailwind is someone who could not get to know CSS very well, CSS is like writing with your hands ,Tailwind is like cutting words from a news paper.
If all companies use tailwind I will find myself another field, Tailwind is just against my mindset
I believe in most projects tailwind can reduce complexity. The benefit is clear, I can think less about naming and structure and focus on component, backed with huge design system. I’m impressed after using it for a week.
Extracting in a @apply is silly indeed. But in most projects, you don’t really need it that much as long as your components are structured well.
Purging CSS I think is the standard now if you want to use any CSS framework, Google hates large CSS. So It’s a plus by making it default.
kudos with the critics. I think what will be challenging is when people using it focusing on design only, but don’t understand the underlying CSS behind. Imagine a component with twenty conflicting CSS classes slapped into it and no documentation.
This can happen too without tailwind, but tailwind just make it easier for design people too wreck it compared to other framework.
I agree Tailwind CSS is for dummies that don’t want to learn CSS or just wanted to “save” time. Yeah, good luck with your “generic” design.
I find the popularity of TailwindCSS surprising. First of all, it’s nothing more than a bunch of shortcuts for inline styles. Everything is local. Ok, first of all – it’s not a new approach. It’s the oldest and the most archaic one.
1. Instead of creating hard-to-maintain CSS, you create hard-to-maintain HTML. You can’t make global changes in your design, e.g., if you want to slightly alternate how your buttons look, it will be much more manual labor. You cannot easily replace “p-8” in your project, sorry.
2. You also create “hard-to-maintain design.” It’s a bad way of thinking about the design. As a designer, you don’t want to micro-manage all components/layouts. You want to create global rules under which the design happens itself. That’s the magic and beauty of designing with CSS. That’s why I often prefer tweaking the look directly in HTML/CSS instead of Sketch.
3. You don’t want buttons to be `red`; you want them to use the `primary` color so that you can change that color in one line of CSS, and it will be applied in dozens of different context it’s being used, etc.
For any designer/front-end programmer that wants to learn the right way of writing CSS, I would recommend BEM. I experimented with many different approaches in many projects of different sizes, and BEM not only allows you to manage your CSS efficiently and long-term but also teaches you the right way of thinking about the design.
Of course, there are many things you learn over years, but TailswindCSS is a dangerous shortcut that prevents you from learning.
There are a lot of misconceptions and inaccuracies in your article and the reader comments that follow about both Tailwind and about how the CSS spec / governing body works.
One of the biggest is with regard to file size. If you’re following best practices, Tailwind css files are not large, in fact can be smaller than what you might be writing yourself when running purgeCss at build to remove unused classes.
I’ve been using Tailwind for a while now. Over the past few months I’ve rejigged my tooling a couple of times and most of the time was spent integrating Tailwind.
I reflected upon why after a few hours today I have achieved little. I wanted to work out where the time went and thought about what I’d been spending my time doing. I’ve got that overpowering enthusiasm to get a new project started but am finding just setting things up is taking ages. After a bit of thought I realised it was Tailwind that had been taking up a lot of my time. I went and Googled “tailwindcss counter productive” and arrived at this article.
I liked the counter-intuitive approach of utility classes being turned upside down philosophically (so to speak) and it seemed to make pretty good sense. Like BEM did with using classes more like IDs. Goes against what you think you know but induces a light bulb moment that is hard to resist. I do use the BEM approach still because it makes good sense.
I’ve been using CSS for 20 years. I’ll go back to using it without Tailwind which is actually more maintainable and easier to do in my experience. Custom properties make CSS a lot more flexible these days anyway.
Thanks for the article!
Read all comments as well as blog. Deciding not to learn tailwind and use it in my college minor project.
Personally, I am done with any Tailwind discussion.
I see comments keep popping up on this post, two today from the same person who seems to wish to remain anonymous as a John Smith. I had to moderate these out because of an offensive e-mail address used.
People are free to use whatever CSS strategy they like. This post is just my experience. I am going to close comments on this post.