Writing Data note

Web Development · 2 min read

Tailwind v4 Missed Every Class in My Shared Package

Tailwind v4 rendered my shared components without their utility classes. One @source directive fixed three hours of monorepo debugging.

In this article3 sections

After I upgraded a pnpm monorepo to Tailwind v4, every component in packages/shared-components lost its utility styles. The app compiled. The class names were present in the markup. Tailwind simply never generated their CSS.

What you see in the browser:

<button className="text-(--btn-icon) bg-(--btn-bg)" />

The classes do not exist, and the raw function syntax just sits there unstyled.

Why It Breaks

Tailwind v4 moved configuration from JS to CSS. JavaScript configuration is no longer detected automatically; an existing config must be loaded explicitly with @config.

Make shared sources explicit Automatic detection depends on the scan base and ignores some paths, including dependencies. In this project it missed the shared package; explicit sources make that boundary clear.

Fix: Add @source

In your app’s CSS file (packages/main-app/src/app.css):

@import 'tailwindcss';
@source '../../shared-components/src';

:root {
  --btn-icon: #222;
  --btn-bg: #cfcfcf;
}

@theme {
  --radius-lg: 0.5rem;
}

The @source directive tells Tailwind where to find classes in other packages.

Multiple packages? Add multiple directives:

@import 'tailwindcss';
@source '../../shared-components/src';
@source '../../shared-utils/src';
@source '../../shared-hooks/src';

:root {
  --btn-icon: #222;
  --btn-bg: #cfcfcf;
}

Your component now works:

export function Button({ children }) {
  return (
    <button className="text-(--btn-icon) bg-(--btn-bg) rounded-(--radius-lg)">
      {children}
    </button>
  );
}

This took me three hours to find because there was no error. The build succeeded; the missing CSS was the only clue.

What Changed

TailwindConfigMonorepo Support
v3JS content: [] arrayWorks if you list all paths
v4CSS @source directiveWorks if you add @source

The path in @source is relative to your CSS file. Tailwind documents the directive in its functions and directives reference.

If I add another shared package later, I also have to add another @source directive. That small piece of explicit configuration is now part of creating a workspace package.

Was this useful?

What was missing?

Thanks for the feedback.