DEV Community

Cover image for I Built an AST-Based Tool to Migrate CSS to Tailwind for React (like frameworks)
Rudra Patel
Rudra Patel

Posted on

I Built an AST-Based Tool to Migrate CSS to Tailwind for React (like frameworks)

Making the Website

A few days ago, I rebuilt my portfolio using Astro (as I wanted to try it). But as a beginner mistake, I used AI from start to finish so it used vanilla CSS. I didn't consider it a problem then because I didn't had to change it now or later.

The problem came when I decided to add blogs to my website. Blogs are dynamic, they have to be fetched from a cms and have to be displayed. Astro is not very good at dynamic content. That forced me to move everything to NextJS. I did most of the migrations myself as it was just to add a

export default function page(props...){
...
return(...)
}
Enter fullscreen mode Exit fullscreen mode

Now that I shifted to NextJS, I wanted to use tailwind instead of vanilla CSS (devs having random thoughts). I didn't want to manually rewrite all my CSS into Tailwind classes. So instead of rewriting styles -

I built a migration tool.


The Problem

The issue I had:

  • External CSS files
  • Class-based styles
  • Hundreds of simple declarations like:
display: flex;
justify-content: center;
font-weight: bold;
margin: 16px;
Enter fullscreen mode Exit fullscreen mode

Rewriting all of that into:

className="flex justify-center font-bold m-4"
Enter fullscreen mode Exit fullscreen mode

was not happening manually. This gave me an idea of creating a vanilla to TW migration tool. Starting my chat with AI, the first thing it said was to not use regex and use AST instead.

Using regex, I just search and replace CSS patterns. But JSX is not plain HTML. It breaks instantly:

<div style={{ display: "flex" }} />
Enter fullscreen mode Exit fullscreen mode

If you regex that, you’ll:

  • Break syntax
  • Miss nested cases
  • Destroy formatting
  • Fail on conditional classNames

So it showed me the correct route.

The Solution: AST-Based Transformation

Instead of treating code as text, AST treats it as structured data. I used:

This allowed me to:

  1. Parse React files into AST
  2. Detect style={{}} objects
  3. Extract properties safely
  4. Convert them to Tailwind utilities
  5. Remove the style attribute
  6. Merge with existing className

All without breaking JSX.

Example

Before

<div style={{ display: "flex", justifyContent: "center" }} />
Enter fullscreen mode Exit fullscreen mode

After

<div className="flex justify-center" />
Enter fullscreen mode Exit fullscreen mode

It also supports:

  • Basic external CSS
  • Simple class selectors
  • className merging
  • Dry-run mode

The Tool

I packaged it as: css-to-tailwind-react

You can install it with:

npm install css-to-tailwind-react
Enter fullscreen mode Exit fullscreen mode
pnpm add css-to-tailwind-react
Enter fullscreen mode Exit fullscreen mode
bun add css-to-tailwind-react
Enter fullscreen mode Exit fullscreen mode

Run:

npx css-to-tailwind-react ./src
Enter fullscreen mode Exit fullscreen mode
pnpm dlx css-to-tailwind-react ./src
Enter fullscreen mode Exit fullscreen mode
bunx css-to-tailwind-react ./src
Enter fullscreen mode Exit fullscreen mode

Current Limitations (in Beta)

It currently skips:

  • Media queries
  • Pseudo selectors (:hover, :focus)
  • Nested selectors
  • SCSS
  • Animations
  • CSS variables

Right now it handles simple and common CSS rules reliably.

The Reason

This wasn’t just about avoiding typing. This was about solving a problem that I faced. Many devs are confused about what should their next project be. Instead of searching the web and asking advice to others they can just create a solution to a problem they face.

Open Source

It’s open source and available here:

GitHub:
http://github.com/vyeos/css-to-tailwind-react

It’s also published on npm.

If you try it on a real project, I’d love feedback.

Especially:

  • Edge cases it breaks
  • CSS patterns it misses
  • Tailwind mappings that feel wrong

What’s Next

Planned improvements:

  • Media query support
  • Pseudo selector conversion
  • CSS Modules support
  • Smarter Tailwind scale matching
  • Interactive CLI mode

This is still beta.

But it works surprisingly well for simple migrations.

If you test it, let me know what breaks 🙂

Top comments (0)