How I Got Tailwind CSS v4 Working with Chakra UI (After 5 Frustrating Bugs)

How I Got Tailwind CSS v4 Working with Chakra UI (After 5 Frustrating Bugs)
Photo by Pankaj Patel / Unsplash

I just wanted to use bg-blue-500 on one button. Four hours later, I was deep in CSS specificity rabbit holes, wondering why my styles kept disappearing.

If you're trying to use Tailwind CSS v4 alongside Chakra UI in a Next.js project, you've probably encountered similar problems. This post shares the exact bugs I ran into—and the fixes that finally worked.

Why Would Anyone Use Both?

You might think I'm crazy. Two CSS frameworks in one project?

But here's the thing. I had a big Chakra UI app. I wanted Tailwind's utility classes for new pages. A full rewrite wasn't an option.

The real reasons to use both:

  • You're slowly moving from Chakra to Tailwind
  • Your team knows different tools
  • You want Chakra's components plus Tailwind's quick styling
  • New features need fast prototyping

What You'll Need

Before we start, make sure you have:

  • A Next.js project with Chakra UI
  • Node.js 18 or newer
  • npm, yarn, or bun

Bug #1: The Config File Lie

The symptom: I had a tailwind.config.js file. But nothing worked.

I spent 30 minutes tweaking the config. Then I checked package.json. The packages weren't installed. The config was completely unused.

The fix:

npm install -D tailwindcss @tailwindcss/postcss postcss autoprefixer

The lesson: A config file means nothing without the packages. Always check package.json first.

Bug #2: The PostCSS Plugin Switcheroo

Here's an important detail most tutorials miss.

Tailwind v4 changed how it works with PostCSS. The old way throws this error:

It looks like you're trying to use `tailwindcss` directly as a PostCSS plugin

What changed: Tailwind v4 moved its PostCSS plugin to a new package called @tailwindcss/postcss.

Create your postcss.config.js like this:

// postcss.config.js
module.exports = {
  plugins: {
    "@tailwindcss/postcss": {},
    autoprefixer: {},
  },
};

Not this (the old v3 way):

// DON'T DO THIS - It's the old v3 way
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

Bug #3: The Invisible Padding

I added p-4 to a div. Nothing happened.

I opened DevTools. The class was there. But a global reset was winning:

* {
  padding: 0;
  margin: 0;
}

Why it broke: CSS without a layer beats CSS with a layer. Tailwind uses layers. My reset didn't.

The fix: Wrap your base CSS in @layer base:

/* src/styles/globals.css */
@layer base {
  * {
    box-sizing: border-box;
    padding: 0;
    margin: 0;
  }

  html,
  body {
    max-width: 100vw;
  }

  a {
    color: inherit;
    text-decoration: none;
  }
}

This tells the browser: "This CSS lives in the same layer system as Tailwind."

Bug #4: The Vanishing Background

This one was very frustrating.

I added bg-primary-500 to a div. The class was there. But DevTools showed backgroundColor: transparent.

What happened: Chakra UI uses Emotion, a CSS-in-JS library. Emotion adds styles after the page loads. Late styles beat early styles.

The fix: Add the important modifier to your Tailwind import:

/* src/styles/globals.css */
@import "tailwindcss" important;
@source "../**/*.{js,jsx,ts,tsx}";

That one word—important—adds !important to all Tailwind styles. Now they win against Emotion's late arrivals.

This is the most important fix in this whole post. Without it, half your Tailwind classes will randomly fail near Chakra components.

Bug #5: The Color Palette Mystery

I built a color grid to test my theme. Some boxes showed colors. Others were blank.

The pattern? Only colors I wrote directly worked. Colors from variables didn't.

The problem: Tailwind's JIT compiler scans your code for class names. It can't see names built from variables:

// BROKEN - JIT can't find this
const bgClass = `bg-${color}-${shade}`;

// WORKS - JIT can find this
const bgClass = "bg-primary-500";

The fix: Use a lookup object with all class names spelled out:

const colorClasses = {
  primary: {
    50: "bg-primary-50",
    100: "bg-primary-100",
    200: "bg-primary-200",
    300: "bg-primary-300",
    400: "bg-primary-400",
    500: "bg-primary-500",
    600: "bg-primary-600",
    700: "bg-primary-700",
    800: "bg-primary-800",
    900: "bg-primary-900",
  },
} as const;

// Use it like this
<div className={colorClasses[color][shade]} />

Yes, it's verbose. But it works every time.

The Complete Setup

Now that you know the bugs, here's the full working setup.

Your globals.css File

/* src/styles/globals.css */

/* The important flag beats Chakra's Emotion styles */
@import "tailwindcss" important;
@source "../**/*.{js,jsx,ts,tsx}";

/* Put all base styles in a layer */
@layer base {
  * {
    box-sizing: border-box;
    padding: 0;
    margin: 0;
  }

  html,
  body {
    max-width: 100vw;
  }

  a {
    color: inherit;
    text-decoration: none;
  }
}

/* Define your shared theme colors */
@theme {
  --font-sans: "Inter Variable", sans-serif;
  --font-heading: "Inter Variable", sans-serif;
  --font-body: "Inter Variable", sans-serif;

  --color-primary-50: #eff6ff;
  --color-primary-100: #dbeafe;
  --color-primary-200: #bfdbfe;
  --color-primary-300: #93c5fd;
  --color-primary-400: #60a5fa;
  --color-primary-500: #3b82f6;
  --color-primary-600: #2563eb;
  --color-primary-700: #1d4ed8;
  --color-primary-800: #1e40af;
  --color-primary-900: #1e3a8a;

  --color-secondary-50: #f0fdf4;
  --color-secondary-100: #dcfce7;
  --color-secondary-200: #bbf7d0;
  --color-secondary-300: #86efac;
  --color-secondary-400: #4ade80;
  --color-secondary-500: #22c55e;
  --color-secondary-600: #16a34a;
  --color-secondary-700: #15803d;
  --color-secondary-800: #166534;
  --color-secondary-900: #14532d;
}

Your postcss.config.js File

// postcss.config.js
module.exports = {
  plugins: {
    "@tailwindcss/postcss": {},
    autoprefixer: {},
  },
};

Your tailwind.config.js File

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ["class"],
  content: [
    "./pages/**/*.{ts,tsx}",
    "./components/**/*.{ts,tsx}",
    "./app/**/*.{ts,tsx}",
    "./src/**/*.{ts,tsx}",
  ],
  theme: {
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    extend: {},
  },
  plugins: [],
};

Quick Debugging Checklist

When things break, check these in order:

  1. Are packages installed? Look at package.json, not just config files
  2. Using the right PostCSS plugin? Must be @tailwindcss/postcss for v4
  3. Did you add important? Required when using Chakra UI
  4. Are base styles in @layer base? Unlayered CSS wins over layered CSS
  5. Building class names from variables? Use a lookup object instead

When to Use Each Framework

After using both together, here's my guideline:

Use Chakra UI for:

  • Modals, drawers, and menus
  • Forms with complex validation
  • Accessible components
  • Pages that already use Chakra

Use Tailwind CSS for:

  • Custom layouts and grids
  • Quick prototypes
  • New standalone features
  • Precise responsive design

Next Steps

You've got the setup. Now put it to work.

Test your integration:

  • Visit your /css test page (or create one)
  • Check that Chakra buttons and Tailwind buttons look consistent
  • Verify colors match across both frameworks

Start migrating gradually:

  • Pick one new feature for Tailwind
  • Keep existing Chakra pages as-is
  • Share colors through @theme to stay consistent

Know the trade-offs:

  • The important flag can cause issues if you need to override Tailwind styles later
  • Dynamic class names will always need lookup objects
  • Bundle size increases slightly with two frameworks

The main lesson? Two CSS frameworks can coexist. You just need to know where the conflicts occur.

Yeah this isn't a good idea.