Tailwind Prose and Content Styling
Use the @tailwindcss/typography plugin to style long-form content like blog posts and documentation with a single prose class and well-chosen overrides.
What you'll learn
- ✓How the typography plugin works
- ✓The prose class and its modifier scales
- ✓Dark mode and color customization
- ✓Overriding specific elements like code blocks and tables
- ✓Tradeoffs versus writing your own content styles
Prerequisites
- •Comfortable with Tailwind utilities and basic HTML
What and Why
Tailwind is utility-first, which is great for components but awkward for long-form content like blog posts, markdown output, or CMS bodies. You do not want to add text-lg leading-7 mt-6 to every paragraph. The @tailwindcss/typography plugin solves this by adding a prose class that applies thoughtful default styles to all the typographic elements inside it. One class on the wrapper, and your markdown looks great.
Mental Model
prose is a single class that cascades a curated stylesheet onto children: headings, paragraphs, lists, blockquotes, code, tables, images, and more. It uses CSS variables so you can override colors and spacing without ejecting the entire ruleset. Sizes scale with modifiers like prose-sm, prose-lg, and prose-xl. Color themes use modifiers like prose-slate or prose-invert for dark mode.
<article class="prose prose-lg prose-slate dark:prose-invert">
h1, h2, p, ul, blockquote, code, table, img
|
v
All styled by the typography plugin
Override with prose-headings:font-display
prose-a:text-blue-600
Hands-on Example
Install the plugin and add it to your config.
npm install -D @tailwindcss/typography
// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/typography')],
};
Then wrap any long-form content.
<article class="prose prose-lg prose-slate mx-auto dark:prose-invert">
<h1>How transformers work</h1>
<p>Attention is a mechanism that lets a model weigh different parts of the input...</p>
<h2>Self-attention in detail</h2>
<ul>
<li>Query, Key, Value projections</li>
<li>Scaled dot product</li>
<li>Softmax over the sequence</li>
</ul>
<blockquote>Attention is all you need.</blockquote>
<pre><code>def attention(q, k, v):
scores = q @ k.T
return softmax(scores) @ v</code></pre>
</article>
The single wrapper produces well-spaced headings, comfortable line lengths, styled lists, code blocks, and a blockquote. Switch to a dark theme by toggling dark:prose-invert, which flips text and link colors using the cascade.
Override individual elements with modifier variants.
<article class="prose prose-blue
prose-headings:font-display
prose-a:no-underline hover:prose-a:underline
prose-code:before:hidden prose-code:after:hidden">
...
</article>
prose-headings: targets every heading. prose-a: targets links. The plugin ships variants for nearly every element you might want to customize.
Common Pitfalls
The biggest pitfall is fighting the plugin instead of working with it. If you find yourself overriding more than a handful of elements, your design probably wants a custom stylesheet rather than prose. The plugin is opinionated on purpose.
Another mistake is nesting components inside prose that you do not want styled. A React component with its own buttons and cards inside a prose wrapper will inherit prose link colors and list styles. Solve this by wrapping rich components in not-prose, which disables the cascade for that subtree.
Watch out for the default code block styling. The plugin adds backtick characters around inline code by inserting them with ::before and ::after. If you use a syntax highlighter that already styles code, hide those pseudo-elements as shown above.
Finally, do not stack prose with conflicting utilities. Setting text-base on a prose-lg wrapper does nothing useful, since the plugin sets its own font sizes through nested rules.
Best Practices
Pick one size like prose-lg and stick with it across your site for visual consistency. Use dark:prose-invert for a one-line dark mode story. Wrap CMS or MDX output in a single prose block at the layout level rather than sprinkling it across components. Use not-prose for sections that contain custom components like callouts, code playgrounds, or product cards.
Customize colors in your config under theme.extend.typography when you want a branded look, rather than scattering element overrides on every page. That keeps your design tokens in one place.
Wrap-up
The typography plugin removes one of the last friction points in a utility-first workflow: styling long-form content. With a single prose class, well-chosen modifiers, and a config-level theme, you get a beautiful reading experience that scales from short notes to book-length docs. Reach for it any time you render markdown or write articles, and let not-prose handle the exceptions.
Related articles
- Tailwind Tailwind Typography Plugin Tutorial
Style long-form Markdown and CMS content with the prose utility, then customize the typography plugin to fit your brand.
- Tailwind Tailwind Animation and Transition Utilities
Animate hover states, page mounts, and component transitions with Tailwind's transition and animation utilities and a few custom keyframes.
- Tailwind Tailwind Arbitrary Values and the JIT Engine
How Tailwind's JIT engine generates classes on demand, when arbitrary values are the right tool, and how to keep your design system tidy.
- Tailwind Tailwind Component Extraction Strategies
When to keep utilities inline, when to extract a component, and how to use @apply, variants, and cva without painting yourself into a corner.