Setting Up Tailwind CSS Typography in a Monorepo

tailwindtypographymonorepocssmarkdown

Building a monorepo with multiple frameworks presents unique challenges, especially when configuring shared dependencies like Tailwind CSS plugins. The Tailwind CSS Typography plugin is essential for styling markdown content, but getting it working correctly across Next.js and Astro apps requires understanding how Tailwind v4's plugin resolution works.

The Challenge

In a monorepo structure, you typically have a shared @workspace/tailwind package that contains your global CSS and Tailwind configuration. This approach centralizes styling and ensures consistency across all applications. However, when you try to add the Typography plugin using Tailwind v4's CSS-first configuration, you'll encounter a common error:

Can't resolve '@tailwindcss/typography' in '/path/to/app/src/styles'

This happens because Tailwind v4 resolves plugins relative to where PostCSS processes the CSS, not where the plugin is declared.

Understanding Tailwind v4 Plugin Resolution

Tailwind CSS v4 introduced a CSS-first configuration approach, moving away from JavaScript config files. Plugins are now declared using the @plugin directive directly in your CSS:

@import "tailwindcss";
@plugin "@tailwindcss/typography";

The key insight is that PostCSS resolves plugins from the context where the CSS file is processed, not where it's declared. In a monorepo, each app processes CSS independently, so plugin declarations must exist in each app's CSS file, even if you're importing shared styles.

The Solution

The correct approach is to declare the plugin in each app's CSS file that imports your shared styles:

Shared Package (packages/tailwind/src/globals.css)

Keep your shared styles clean without plugin declarations:

@import "tailwindcss";
@import "tw-animate-css";

@source "../../ui/src/**/*.{ts,tsx}";
@source "../../../apps/web/**/*.{ts,tsx}";
@source "../../../apps/astro-app/**/*.{astro,ts,tsx}";

/* Your shared theme variables, colors, etc. */

Next.js App (apps/web/app/globals.css)

Create a local CSS file that imports shared styles and declares the plugin:

@import "@workspace/tailwind/globals.css";
@plugin "@tailwindcss/typography";

Astro App (apps/astro-app/src/styles/globals.css)

Similarly, declare the plugin in Astro's CSS file:

@import "@workspace/tailwind/globals.css";
@plugin "@tailwindcss/typography";

@source "../../../packages/ui/**/*.{ts,tsx}";
@source "../**/*.{astro,ts,tsx}";

Installation Requirements

The plugin must be installed in three places:

  1. Shared Package (packages/tailwind/package.json): As a dependency so it's available in the workspace
  2. Each App: Install in both apps/web and apps/astro-app so PostCSS can resolve it during build
# Install in shared package
pnpm add @tailwindcss/typography --filter tailwind

# Install in each app
pnpm add @tailwindcss/typography --filter web
pnpm add @tailwindcss/typography --filter astro-app

Why This Works

When PostCSS processes CSS in each app:

  1. It reads the app's local CSS file (e.g., apps/web/app/globals.css)
  2. Encounters the @plugin "@tailwindcss/typography" declaration
  3. Resolves the plugin from the app's own node_modules directory
  4. Processes the imported shared CSS with the plugin active

This ensures that plugin resolution happens in the correct context while still maintaining shared styles and configuration.

Benefits of This Approach

  • Centralized Styling: Shared theme variables, colors, and utilities remain in one place
  • Framework-Specific Plugins: Each app can declare plugins it needs without affecting others
  • Consistent Typography: The prose classes work identically across all apps
  • Maintainable: Changes to shared styles propagate automatically

Using Typography Classes

Once configured, you can use Typography classes throughout your applications:

<article className="prose prose-lg dark:prose-invert max-w-none">
  {/* Your markdown content */}
</article>

The plugin provides beautiful typographic defaults for headings, paragraphs, lists, code blocks, and more, with built-in dark mode support.

Conclusion

Setting up Tailwind CSS Typography in a monorepo requires understanding how Tailwind v4 resolves plugins. By declaring plugins in each app's CSS file while maintaining shared styles in a central package, you get the best of both worlds: consistent styling and framework-specific flexibility. This pattern works for any Tailwind plugin, making it a valuable approach for monorepo architecture.