Tailwind CSS Typography Plugin: Using prose Classes Correctly
AI generated
</>
tw
Tailwind CSS · Typography Plugin · prose · CMS Content
Tailwind CSS Typography Plugin
Understanding and correctly using prose classes

Styling CMS content without the Tailwind Typography Plugin means manually overriding hundreds of reset rules. The @tailwindcss/typography plugin solves this problem with the prose class, while also giving developers precise control over every typographic detail.

12 min read prose · not-prose · modifiers · dark mode · Tailwind v3 & v4 @tailwindcss/typography · CMS · Markdown

1. The problem: Tailwind reset and CMS content

Tailwind CSS resets all browser default styles with its Preflight stylesheet. This is intentional, it ensures consistent behavior across browsers and gives developers a clean baseline. For UI components built with Tailwind classes, this works perfectly. For CMS content that arrives as raw HTML from an editor, it becomes a problem: headings no longer look like headings, lists lose their bullets, links lose their color. Anyone who then manually brings CMS content back to normal typography with Tailwind classes ends up writing a manual reset of the reset.

This is exactly where the Tailwind CSS Typography Plugin comes in. The official plugin @tailwindcss/typography adds the prose class, which is applied to a parent container. All child elements, h1 through h6, p, ul, ol, blockquote, code, pre, table, img, and many more, automatically receive carefully considered typographic styles. The plugin is designed for markdown-rendered content, blog posts, documentation, and any kind of editorial content where you have no direct control over the HTML structure.

2. Installing and configuring the Typography Plugin

The Tailwind CSS Typography Plugin is installed via npm and then registered in the Tailwind configuration. In Tailwind v3 this happens in tailwind.config.js. In Tailwind v4 with the CSS-first approach, the plugin is included directly in the CSS file. The plugin itself is officially maintained by Tailwind Labs and kept in sync with Tailwind core, so breaking changes are unlikely.

After installation and configuration, all prose classes are immediately available. No further steps are needed, no custom CSS, no manual selectors. That is the central benefit of the Typography Plugin: it works out of the box for the most common use case while also offering a structured API for customization. In projects with a build step, for example with Vite or Webpack, the plugin is automatically re-evaluated on every change to the configuration.


/* tailwind.css - Tailwind v4 CSS-first configuration */
@import "tailwindcss";

/* Import the typography plugin for prose classes */
@plugin "@tailwindcss/typography";

/* Custom prose theme overrides - scoped to the prose selector */
@layer base {
  :root {
    --tw-prose-body: theme(colors.slate.700);
    --tw-prose-headings: theme(colors.slate.900);
    --tw-prose-links: theme(colors.sky.600);
    --tw-prose-code: theme(colors.sky.700);
    --tw-prose-pre-bg: theme(colors.slate.900);
  }
}

3. The prose class: what it does and why

The prose class is a CSS namespace wrapper. The plugin generates a large set of CSS selectors that all follow the pattern .prose > *, .prose h1, .prose p, and so on. These selectors undo the Preflight resets and lay down carefully tuned typographic rules: line height, spacing, font sizes, colors, and margins for all relevant HTML elements. The crucial point: these rules apply only inside the element carrying the prose class.

This means the Tailwind Typography Plugin acts strictly locally. UI components outside the prose container remain completely untouched by the plugin. You can build an elaborate design system with Tailwind and simultaneously style a CMS content area with prose, without any fear of conflicts. Internally the plugin works with CSS custom properties that define the theme, which makes later customization very elegant, since you only need to override the custom properties instead of duplicating selectors.

4. Size modifiers: prose-sm to prose-2xl

The Tailwind CSS Typography Plugin ships five prebuilt size modifiers: prose-sm, prose-base, prose-lg, prose-xl, and prose-2xl. These modifiers do not just adjust the font size, they scale all typographic measurements proportionally: heading sizes, spacing between elements, code block padding, and table formatting are all resized coherently. This is considerably more refined than simple font scaling.

In practice you use prose-base for the standard case, prose-lg for highly readable blog articles, and prose-sm for compact documentation areas or sidebars. Combining it with responsive prefixes is fully possible: prose prose-sm md:prose-base lg:prose-lg adapts the typography to the screen size. The Typography Plugin supports all Tailwind breakpoint prefixes without additional configuration, the modifiers are fully available as responsive classes.

5. not-prose: excluding elements from the typography scope

In practice you regularly run into a case where you want to embed a UI component inside a CMS content area that should not be affected by prose styling: a call-to-action banner, a product card, an interactive demo, or a code block with its own formatting. This is where the not-prose class comes in. It breaks the Typography Plugin styling for the element and all its child elements.

The mechanism behind it is elegant: the plugin generates an additional negation for all its selectors that excludes not-prose. An element with class="not-prose" inside a prose container receives none of the plugin's typographic rules. Important to understand: not-prose must be set directly on the element you want to exclude, it is not enough to set it on an outer wrapper element when Tailwind selectors target child elements directly. This very pattern is used in this article itself: all non-text blocks such as the hero, the table of contents, and the summary card carry the not-prose class.


<!-- CMS content area with embedded UI component -->
<div class="prose prose-lg max-w-none">
  <h1>Product description</h1>
  <p>This article explains the Typography Plugin in full.</p>

  <!-- UI component that must NOT be styled by prose -->
  <div class="not-prose bg-sky-50 border border-sky-200 rounded-xl p-6 my-8">
    <div class="flex items-center gap-4">
      <div class="w-12 h-12 bg-sky-500 rounded-full flex items-center justify-center text-white font-bold">
        TW
      </div>
      <div>
        <p class="font-bold text-sky-900 text-lg m-0">Tailwind CSS course</p>
        <p class="text-sky-700 text-sm m-0">All prose classes explained in one course</p>
      </div>
    </div>
    <!-- Tailwind classes work normally here - no prose override -->
    <a href="/contact" class="mt-4 inline-block bg-sky-600 text-white px-6 py-2 rounded-lg font-semibold text-sm hover:bg-sky-700 transition-colors">
      Go to course
    </a>
  </div>

  <p>The text here is styled normally by prose again.</p>
</div>

6. Customizing the prose class: theme() configuration

The Tailwind CSS Typography Plugin is fully customizable. The foundation for customization is CSS custom properties, which the plugin uses internally for all typographic decisions. Instead of overriding selectors, you simply set the corresponding custom property again. The plugin defines variables for body text color, heading color, link color, code color, code block background color, border colors, and many other aspects. In Tailwind v4 with the CSS-first approach, you set these variables directly in the CSS.

For project-specific prose themes, you can create your own prose variants. In Tailwind v3 this happens via the typography key in the theme configuration, where you define named configuration objects. A prose-brand modifier is then usable with <div class="prose prose-brand">. This extensibility makes the Typography Plugin the right solution even for projects with their own design system, you do not have to choose between the plugin and your own branding, you can combine both.


/* Custom prose theme - overriding typography plugin CSS variables */
@layer base {
  /* Default prose theme (light mode) */
  .prose {
    --tw-prose-body: theme(colors.slate.700);
    --tw-prose-headings: theme(colors.slate.900);
    --tw-prose-lead: theme(colors.slate.600);
    --tw-prose-links: theme(colors.sky.600);
    --tw-prose-bold: theme(colors.slate.900);
    --tw-prose-counters: theme(colors.sky.500);
    --tw-prose-bullets: theme(colors.sky.400);
    --tw-prose-hr: theme(colors.slate.200);
    --tw-prose-quotes: theme(colors.slate.900);
    --tw-prose-quote-borders: theme(colors.sky.400);
    --tw-prose-captions: theme(colors.slate.500);
    --tw-prose-code: theme(colors.sky.700);
    --tw-prose-pre-code: theme(colors.sky.200);
    --tw-prose-pre-bg: theme(colors.slate.900);
    --tw-prose-th-borders: theme(colors.slate.300);
    --tw-prose-td-borders: theme(colors.slate.200);
  }
}

7. Dark mode with prose-invert

Dark mode support is built into the Tailwind CSS Typography Plugin. The class prose-invert switches the entire color palette to a variant optimized for dark backgrounds. Text, headings, links, code, and all other colored elements are automatically switched to bright, high-contrast tones. Combined with Tailwind's dark mode prefix, this is used like so: prose dark:prose-invert. The prefix ensures that prose-invert is only activated once the user has enabled dark mode.

The dark mode colors of prose-invert can be customized exactly like the light mode colors. The plugin defines a corresponding invert variable for every light mode variable: --tw-prose-invert-body, --tw-prose-invert-headings, --tw-prose-invert-links, and so on. You set these analogously to the light mode variables. A complete brand-consistent dark mode typography theme can be realized with roughly twenty custom property definitions, without writing a single custom CSS selector. That is the core benefit of the Typography Plugin over manual solutions.

8. prose vs. manual typography: a direct comparison

The alternative to prose is manually controlling every typographic aspect yourself. In practice that means writing custom styles for every HTML tag inside the content area that override the Preflight reset. That is possible, but tedious and error-prone, especially when new tags are added or the design changes. A comparison of both approaches shows why the Tailwind CSS Typography Plugin is the superior choice for CMS content.

Aspect Manual without plugin With prose / Typography Plugin
Effort Many custom selectors per tag A single prose class
Maintenance Every design change propagated manually Change custom properties centrally
Dark mode Duplicate selectors for dark: dark:prose-invert is enough
Responsive sizes Manual breakpoint overrides prose-sm md:prose-base lg:prose-lg
New HTML tags Must be styled manually Plugin covers all common tags

9. Typography Plugin in Tailwind v4

Tailwind v4 brings the CSS-first approach: the entire configuration moves out of the JavaScript file into CSS. The Tailwind CSS Typography Plugin has been prepared for this approach since version 0.5.x. In Tailwind v4 the plugin is included directly in the main CSS file with @plugin "@tailwindcss/typography". Installing the plugin via npm stays the same. Customizations are no longer made in tailwind.config.js under the typography key, but directly via CSS custom properties in the CSS file.

The difference in workflow is substantial: in v3 you had to go into the JavaScript configuration for a prose color adjustment, formulate the value as a function reference against the Tailwind theme, and then restart the build. In v4 a color adjustment is a simple --tw-prose-links: #0369a1; in the CSS file, visible immediately, with no build restart needed if hot reloading is active. For teams that previously adjusted the Typography Plugin through the JavaScript configuration, migrating to the CSS approach in v4 is a simplification, not extra work.


/* tailwind.css - Full Tailwind v4 typography plugin setup */
@import "tailwindcss";
@plugin "@tailwindcss/typography";

/* Custom prose variant "hyva" for Hyvä Themes integration */
@layer utilities {
  .prose-hyva {
    --tw-prose-body: theme(colors.gray.700);
    --tw-prose-headings: theme(colors.gray.900);
    --tw-prose-links: theme(colors.sky.600);
    --tw-prose-code: theme(colors.sky.700);
    --tw-prose-pre-bg: theme(colors.slate.900);
    --tw-prose-pre-code: theme(colors.sky.200);
    --tw-prose-quote-borders: theme(colors.sky.400);
    --tw-prose-bullets: theme(colors.sky.500);
    --tw-prose-counters: theme(colors.sky.500);
    /* Invert variables for dark mode via dark:prose-invert */
    --tw-prose-invert-body: theme(colors.slate.300);
    --tw-prose-invert-headings: theme(colors.white);
    --tw-prose-invert-links: theme(colors.sky.400);
    --tw-prose-invert-code: theme(colors.sky.300);
  }
}

10. Summary

The Tailwind CSS Typography Plugin with the prose class is the right solution for any use case where HTML content from an external source, a CMS, a markdown renderer, an API, needs to be styled typographically without direct control over the HTML structure. It elegantly solves the Preflight problem without undermining Tailwind's utility-first approach. Its combinability with dark mode via prose-invert, the responsive size modifiers, and full customizability through custom properties make it the tool of first choice.

The most important operational aspect: consistently applying not-prose to embedded UI components. Anyone who forgets this ends up puzzled by strange-looking buttons or cards inside CMS areas. In Tailwind v4 projects, adopting the CSS-first approach for plugin configuration pays off, it simplifies customization considerably and keeps all typography decisions in one place in the CSS, instead of spreading them between JavaScript and CSS files.

Tailwind Typography Plugin: the essentials at a glance

prose class

Set on the parent container, all HTML child elements receive carefully considered typographic styles. Acts strictly locally, no effect on outside UI components.

not-prose

Indispensable for embedded UI blocks, CTAs, and cards inside prose containers. Prevents the plugin from overriding Tailwind classes.

Dark mode

dark:prose-invert automatically switches all colors to dark variants. Customizable via --tw-prose-invert-* custom properties.

Tailwind v4

@plugin "@tailwindcss/typography" directly in the CSS. Customizations via custom properties instead of JavaScript configuration, simpler, faster, closer to the CSS.

11. FAQ: Tailwind CSS Typography Plugin and prose

1What is the Tailwind CSS Typography Plugin?
The @tailwindcss/typography plugin provides the prose class. It applies carefully considered typographic styles to all HTML child elements, ideal for CMS content, markdown, and documentation.
2What do I need not-prose for?
not-prose removes an element from the typography styling. Needed for UI components inside prose containers that carry their own Tailwind classes and should not be overridden.
3How do I enable dark mode for prose?
With dark:prose-invert combined with prose. The plugin switches all colors to dark variants. Customizable via --tw-prose-invert-* custom properties.
4What are the size modifiers?
prose-sm, prose-base, prose-lg, prose-xl, and prose-2xl scale all typographic measurements proportionally. Fully usable responsively: prose md:prose-lg lg:prose-xl.
5How do I customize the link color in prose?
In Tailwind v4: set the --tw-prose-links custom property. In v3: theme.extend.typography in tailwind.config.js. Both approaches only change link colors in the prose context.
6Does the plugin work with Tailwind v4?
Yes. In v4, include it with @plugin "@tailwindcss/typography" in the CSS. Customizations happen via custom properties instead of JavaScript configuration.
7Does prose affect components outside?
No. The plugin generates selectors that apply exclusively inside the prose container. Everything outside remains completely untouched.
8Create custom prose variants?
In v4: define a CSS class with --tw-prose-* properties, combined with prose. In v3: extend the typography key in the Tailwind configuration.
9How does prose handle code blocks?
Inline code gets background color and padding. Multi-line blocks get a dark background and horizontal scrolling. Customizable via --tw-prose-pre-bg and --tw-prose-code.
10For which projects is the plugin needed?
For all projects with CMS content, markdown, or HTML from external sources. For pure UI projects without free-text content, the plugin is not required.