next.jstailwind-cssmonorepopostcssturborepo

Tailwind CSS not applying in Turborepo monorepo with shared tailwind-config package and UI component library and storybook


Description

I have a Turborepo monorepo set up with the following structure:

root/
├── apps/
│   ├── web/                  # Next.js app
│   │   ├── src/
│   │   ├── package.json
│   │   ├── tailwind.config.js   # Imports shared config
│   │   └── postcss.config.mjs   # Imports shared config
│   ├── docs/                 # (optional docs app)
│
├── packages/
│   ├── ui/                   # UI component library
│   │   ├── src/
│   │   │   └── styles.css
│   │   ├── dist/
│   │   │   ├── index.js
│   │   │   └── styles.css
│   │   ├── package.json
│   │
│   ├── tailwind-config/      # Shared Tailwind config package
│   │   ├── tailwind.config.js
│   │   ├── postcss.config.mjs
│   │   └── package.json
│
└── package.json (root)

Root package.json

{
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "devDependencies": {
    "tailwindcss": "^4.1.12",
    "@tailwindcss/postcss": "^4.1.12",
    "postcss": "^8.5.6",
    "turbo": "^2.5.4"
  }
}

Shared Tailwind Config (packages/tailwind-config/tailwind.config.js)

import path from "path";

export default {
  content: [
    path.join(__dirname, "../../apps/**/*.{js,ts,jsx,tsx}"),
    path.join(__dirname, "../../packages/**/*.{js,ts,jsx,tsx}")
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Shared PostCSS Config (packages/tailwind-config/postcss.config.mjs)

export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

UI Package (packages/ui/package.json)

{
  "name": "@astra/ui",
  "version": "1.0.0",
  "main": "./dist/index.js",
  "sideEffects": ["**/*.css"],
  "files": ["dist"],
  "scripts": {
    "build:css": "tailwindcss -i ./src/styles.css -o ./dist/styles.css",
    "dev:css": "tailwindcss -i ./src/styles.css -o ./dist/styles.css --watch",
    "build": "npm run build:css && tsc"
  }
}

Web App Tailwind Config (apps/web/tailwind.config.js)

import config from "@repo/tailwind-config/tailwind.config.js";
export default config;

Web App PostCSS Config (apps/web/postcss.config.mjs)

import config from "@repo/tailwind-config/postcss.config.mjs";
export default config;

Web App Global CSS (apps/web/app/global.css)

@import "@astra/ui/dist/styles.css";

The Problem

What I Tried

Question

Why aren’t Tailwind styles applying in my Next.js app when using:


Solution

  • No AI will ever be as good as well-written, easy-to-digest documentation.

    TailwindCSS v4

    From v4 onwards, there is no tailwind.config.js by default. This is why you don't see the shared configuration.

    From v4 onwards, in CSS-first configuration, it's recommended to declare the sources using @source, and - if absolutely necessary - the configuration files using the @config directive, strictly with a relative path.

    Something like this:

    apps/web/app/global.css

    @import "tailwindcss"; /* instead of @tailwind directives */
    
    /* Sources instead of content property */
    @source "./../../../apps/**/*.{js,ts,jsx,tsx}";
    @source "./../../../packages/**/*.{js,ts,jsx,tsx}";
    
    /* Configurations - If possible, avoid it; */
    /* Instead, migrate the external configuration into a CSS-first setup as well, and simply import the external CSS-first configuration into the current one using a standard CSS @import */
    @config "./../../../path/to/@repo/tailwind-config/tailwind.config.js";
    
    /* Another CSS-first configurations */
    @theme {
      /* ... */
    }
    

    Review

    Tried [...] running tailwindcss -i ./src/styles.css -o ./dist/styles.css inside packages/ui.

    Why? You're using a PostCSS plugin. Why would you want to compile with the CLI? Starting from v4, partly because of things like this, the main TailwindCSS engine, the CLI module, and the PostCSS plugin were separated. They also released a new Vite plugin.

    package.json

    {
      "scripts": {
          "build:css": "tailwindcss -i ./src/styles.css -o ./dist/styles.css",
          "dev:css": "tailwindcss -i ./src/styles.css -o ./dist/styles.css --watch",
        "build": "npm run build:css && tsc"
      }
    }
    

    For the reasons mentioned above, the presence of build:css and dev:css here also doesn't make sense. You've installed the PostCSS plugin, so you need to integrate PostCSS into your system, and it can typically be run with the npm run dev or npm run build commands.

    Otherwise, I don't see the point of using PostCSS. If you want to use the CLI, then the package to install is @tailwindcss/cli and the command to use is npx.

    Or when using the Standalone version, PostCSS is not needed either.

    TailwindCSS v3

    Or you can revert to TailwindCSS v3, where the JS-based configuration is still easily accessible.

    npm remove @tailwindcss/postcss
    npm install tailwindcss@3
    

    In v4, however, the goal is a full transition to CSS-first, so sooner or later the JS configuration is expected to be discontinued - similar to when the JIT engine was introduced in v2.