In the previous article, we got familiar with the business case behind the migration from Bootstrap to Tailwind CSS, the two libraries and the difference between them. I also shared my personal opinion of using the two technologies.

In this article, I will discuss the pros and cons of the architectural strategies we considered during the migration.

Starting point

Prior to the migration, we had a single-page app (SPA) created with Create React App (CRA) and the following packages:

  • React 16
  • Bootstrap 4, React-Bootstrap
  • Classnames, Polished, Emotion (styled-components)
  • React-Query, Axios
  • Jest, React-Testing-Library, MSW
  • Formik
  • and others

To get the most out of this article, you need to be familiar with these technologies.

Research phase

After some ideation, we came up with four strategies/options to migrate from Bootstrap to Tailwind CSS. We evaluated each by listing the positive and negative sides and creating a quick SPIKE to have a broader view of the potential pitfalls. This way, we can make the most informed decision.

💡 Look before you leap.

compass unsplash

Strategy 1: Complete rewrite of the app

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 UI library.
  • no value for the end customers - For some time, we won’t be able to implement new features and deliver any customer value.

Strategy 2: Update Bootstrap’s variables and theme

so they resonate with Tailwind CSS’s default theme and 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 in the end 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.
  • big 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 🍝.

Strategy 3: Create a Component Library

based on our Design Language System (DLS) with Tailwind CSS and use it throughout the different client projects, starting from the current one.

Pros (➕):

  • Design Language System ready to be used throughout the different projects and products - Having 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 clean code. Everything will be decoupled and properly configured.

Cons (➖):

  • time and $$$ to develop and integrate
  • no value for the end customers

Strategy 4: Integrate Tailwind CSS

and use it on top of Bootstrap. As part of this strategy we decided to look for opportunities to extract reusable atomic React components styled with Tailwind CSS and prepare for the DLS.

Pros (➕):

  • customer value
  • save time and $$$
  • remove Bootstrap later - By rightly integrating Tailwind CSS, we can lay the foundations for later removing Bootstrap with ease.

Cons (➖):

  • class and style mismatches
  • messy and ugly
  • anti-pattern - That’s an anti-pattern because we will be using two completely different libraries for the same thing (styling) in the same project.

In the end, we decided to go with Strategy 4. 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.

what could possibly go wrong

The enablers

Going through an extensive exploration phase, we found two enablers for implementing Strategy 4. These are:

  1. Tailwind CSS’s prefix and preflight configurations.

    • We can add a custom prefix to all Tailwind CSS classes, so we don’t have any collisions with the Bootstrap ones.

    • Disable the default styling.

  2. twin.macro.

    • We can use CSS classes with our styled-components.

Because of them, we managed to create the component below:

const StyledBadge = styled('span')`
	${tw`ms-px-2 ms-text-amber-300 ...`}

const Badge = (...) => {
  return (
    <StyledBadge ...>

For more on the implementation, see part 3 of the series.


To summarize, before tackling a problem or proceeding with an architectural decision, it’s a good practice to spend some time planning, researching and developing in a time-boxed manner. This way, the chances of getting the most suitable decision for your case increase.

💡 There’s no silver bullet for every problem or situation. That’s why good exploration sets the path to success. In the end, it’s all about tradeoffs.

Stay tuned for the next article in the series, where I will go through the execution part of the chosen strategy, how we leveraged the enablers and some anti-patterns we went through.