2

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

  • The UI components from @astra/ui render, but none of the Tailwind CSS styles are applied.

  • Even default Tailwind utility classes in apps/web are not working (e.g., bg-red-500 does nothing).

  • Breakpoints (sm:, md:, etc.) also do not apply.

  • The Tailwind CLI build in @astra/ui does generate dist/styles.css, but when imported in the web app, styles are still missing.

  • PostCSS and Tailwind config are both imported from the shared @repo/tailwind-config package.

What I Tried

  • Deleting node_modules, .turbo, and dist folders and reinstalling dependencies.

  • Running tailwindcss -i ./src/styles.css -o ./dist/styles.css inside packages/ui.

  • Verifying that content paths in the shared tailwind.config.js include both apps and packages.

  • Making sure "sideEffects": ["**/*.css"] is set in
    packages/ui/package.json so CSS isn’t tree-shaken.

  • Importing the built CSS in apps/web/app/global.css.

Question

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

  • A Turborepo monorepo

  • A shared Tailwind config package

  • A separate UI component library that outputs built CSS?

  • What is the correct way to configure Tailwind so styles are applied
    across both the web app and the UI package in this setup?

1 Answer 1

0

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.

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.