How to Guide: Migrate a React Project from Bootstrap to Tailwind CSS
A software architecture migration of a React SPA from Bootstrap to Tailwind CSS (6 min)
A few years ago I was part of a leading team responsible for migrating a client’s React SPA from Bootstrap to Tailwind CSS.
We had two crucial business constraints:
continue to develop new features
update the overall look and feel of the application by integrating a new design system based on Tailwind CSS
As Senior Engineers, we approached the problem strategically.
We outlined the pros and cons of several architectural strategies we considered as solutions to migrate from Bootstrap to Tailwind CSS and satisfy the key business constraint.
In this article, I’ll share the architectural strategies and the one we decided to execute.
All stakeholders were happy with the end result.
The Starting Point
A single-page app (SPA) created with Create React App (CRA) and the following packages:
The Research Phase
It’s always a good practice to spend time planning, researching, estimating, and developing in a time-boxed manner, before starting to tackle a problem.
This will increase the chances of getting the most suitable decision for your case.
After some ideation, we came up with four solutions to migrate from Bootstrap to Tailwind CSS.
We evaluated each by listing the positive and negative sides and creating a quick SPIKE for a broader view of the potential pitfalls.
💡 Look before you leap.
Solution 1: Complete rewrite of the app
The first solution was a complete rewrite of the React Application, from Bootstrap to Tailwind CSS.
Pros 👍:
nice, sweet, and clean - We won’t have any dirty stuff and workarounds by rebuilding the project from scratch.
Cons 👎:
time & $$$ - It will cost a lot of time and money to rebuild the project using the new design system.
no value for the end customers - We won’t be able to implement new features and deliver any customer value for some time.
Solution 2: Update Bootstrap’s variables and theme
The second solution was to update Bootstrap’s variables and theme to resonate with Tailwind CSS‘s default theme. This way we can continue developing new features with Tailwind CSS. For more info, check this section in the previous article.
Pros 👍:
customer value - We can deliver customer value and features while integrating and using the two frameworks.
save time and $$$ (maybe) - Instead of completely rewriting the app, we can integrate and use both libraries incrementally. We will waste considerable time and money if we have the bugs mentioned in the cons section.
Cons 👎:
class and style mismatches - We might end up having classes with the same names from the two libraries leading to unexpected styles and making it hard to debug.
hard to update the theme - There are just too many things to cover and there’s always the risk of missing something.
significant risk for potential issues - The risk for future problems and hard debugging is enormous.
hard to replace Bootstrap later - By mixing both libraries in our components and pages, it will become complicated to remove Bootstrap after that because everything will be coupled → spaghetti code 🍝.
Solution 3: Create a Component Library
The third solution was to create a Component Library based on our Design Language System (DLS) with Tailwind CSS and use it throughout the different client’s projects, starting from the current one.
Pros 👍:
Design Language System ready to be used throughout the different projects and products - A DLS will be a huge benefit, so we can have the same look and feel throughout the different customer product lines.
nice, sweet, and clean - We will have a clean code. Everything will be decoupled and properly configured.
Cons 👎:
time and $$$ to develop and integrate
no value for the end customers
Solution 4: Integrate Tailwind CSS
The fourth solution was to integrate Tailwind CSS and use it together with Bootstrap.
Pros 👍:
customer value
save time and $$$
remove Bootstrap later - By rightly integrating Tailwind CSS, we can lay out the foundations for later removing Bootstrap with ease.
prepare for the DLS - By creating reusable and atomic React components styled with Tailwind CSS, we can easily move them into a separate UI library later.
Cons 👎:
class and style mismatches
messy and ugly
anti-pattern - That’s an anti-pattern because we will use two completely different libraries for the same thing (styling) in the same project.
In the end, decided to go with Solution 4.
This way we can start using Tailwind CSS, update the pages step by step, and simultaneously deliver new features to the end customers so we don’t stop providing customer value.
The Execution
Before starting the implementation, we revisited how we use react-bootstrap
in the project.
We saw we reference it throughout the whole codebase.
This was another anti-pattern we had in our project.
Isolate the 3rd party library (react-bootstrap)
By isolating react-bootstrap
in our codebase, we can later replace one component from the package with our implementation.
We can do that without updating the files where we use the component.
I’ve shared more about this here in the following article:
Integrate Tailwind CSS
We followed the tailwindcss installation guide to install and configure it in our project.
But, we made two crucial changes in our configuration:
We turned off the preflight mode to disable the default styling coming from Tailwind CSS.
We added a custom ms- prefix to all Tailwind CSS classes, so we don’t have any collisions with the Bootstrap ones.
This enabled us to use classes both from Bootstrap and Tailwind CSS at the same time without interference.
For example:
mt-2 and border are classes from Bootstrap whereas ms-px-2 and ms-text-amber-300 are classes from Tailwind CSS.
Develop new React Components with Tailwind CSS
We developed each new feature with Tailwind CSS to minimize the number of components we have to rewrite from Bootstrap to Tailwind CSS in the future.
One key aspect of this step was using twin.macro to have tailwindcss classes in our styled-components.
We also sought opportunities to extract reusable React components styled with Tailwind CSS.
Every time we had to reuse a component from react-bootstrap we tried to rewrite it with Tailwind CSS without changing the components’s APIs.
We followed the ones from react-bootstrap
because we didn’t want to introduce many changes at once, only style updates.
⭐ TL;DR
There’s no silver bullet for every problem or situation.
A good exploration sets the path to success.
We decided to use both Bootstrap and Tailwind CSS simultaneously so we can continue developing new features and update the overall look and feel of the app.
We accepted the anti-pattern of using two UI frameworks.
Sometimes, to go from A to B, you have to through A → C → D → B. Strive for incremental improvements.
In the end, it’s all about tradeoffs.
👋 Let’s connect
You can find me on LinkedIn or Twitter.
I share daily practical tips to level up your skills and become a better engineer.
Thank you for being a great supporter, reader, and for your help in growing to 12.1K+ subscribers this week 🙏
This newsletter is funded by paid subscriptions from readers like yourself.
If you aren’t already, consider becoming a paid subscriber to receive the full experience!
Think of it as buying me a coffee twice a month, with the bonus that you also get all my templates and products for FREE.
You can also hit the like ❤️ button at the bottom to help support me or share this with a friend to get referral rewards. It helps me a lot! 🙏
It's important not to fall into the hype of rewriting from scratch. What seems like a great idea now, could end with a never-ending migration process
Also, thanks for the mention, Petar!