Setting Up Tailwind CSS Typography in a Monorepo
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:
- Shared Package (
packages/tailwind/package.json): As a dependency so it's available in the workspace - Each App: Install in both
apps/webandapps/astro-appso 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:
- It reads the app's local CSS file (e.g.,
apps/web/app/globals.css) - Encounters the
@plugin "@tailwindcss/typography"declaration - Resolves the plugin from the app's own
node_modulesdirectory - 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
proseclasses 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.