I'm using Next.js 15 with Tailwind CSS V3 and the react-markdown package to render AI chat output inside a component.
I've customized the markdown rendering to replace all <h1>
, <h2>
, <h3>
elements with styled <div>
s using Tailwind classes to avoid default typography spacing issues.
Despite this, there’s still excessive vertical space between elements — particularly under h1
, h2
, and h3
replacements. I've tried removing all margin-bottom
and adding mb-0
, mt-0
, etc., but spacing persists.
Here’s what it looks like (I’ve added debug background colors and annotations):
15
19
react-markdown@9
v3
remark-gfm
rehype-highlight
Minimal vertical spacing between elements in the chat bubbles.
Headings and their following content have unwanted gaps that don’t collapse.
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeHighlight]}
components={{
h1: ({ node, ...props }) => (
<div className="text-lg font-bold bg-pink-200 mt-2 mb-0" {...props} />
),
h2: ({ node, ...props }) => (
<div className="text-base font-semibold bg-pink-200 mt-2 mb-0" {...props} />
),
h3: ({ node, ...props }) => (
<div className="text-base font-medium bg-pink-200 mt-2 mb-0" {...props} />
),
p: ({ node, ...props }) => (
<p className="mt-[2px] mb-[2px] bg-red-400" {...props} />
),
ul: ({ node, ...props }) => (
<ul className="pl-5 my-[2px] list-disc bg-limegreen" {...props} />
),
li: ({ node, ...props }) => (
<li className="my-[2px] bg-cornflowerblue" {...props} />
),
code: ({ node, ...props }) => (
<code className="bg-black/5 px-1 rounded text-[13px]" {...props} />
),
pre: ({ node, ...props }) => (
<pre className="bg-orange p-2 rounded my-2 overflow-x-auto" {...props} />
)
}}
>
{msg.text}
</ReactMarkdown>
<div>
tagsmb-0
, mt-0
[+ *]
in TailwindIs there something about react-markdown
’s structure or the way it parses elements that still causes vertical gaps between custom heading components and their following siblings?
Do I need to wrap children or flatten block structure?
Any tips would be greatly appreciated.
<div class="markdown">
<p class="bg-red-400 mb-[2px] mt-[2px]">
<code class="rounded bg-black/5 px-1 text-[13px]">useMemo</code> and
<code class="rounded bg-black/5 px-1 text-[13px]">useCallback</code> are both hooks provided by
React to optimize performance by memoizing values or functions. Let's break down the differences
and when to use each:
</p>
<div class="bg-pink-200 mb-0 mt-2 text-base font-medium">useMemo</div>
<p class="bg-red-400 mb-[2px] mt-[2px]">
<strong>Purpose:</strong> <code class="rounded bg-black/5 px-1 text-[13px]">useMemo</code> is
used to memoize a computed value.
</p>
<p class="bg-red-400 mb-[2px] mt-[2px]"><strong>Syntax:</strong></p>
<pre
class="bg-orange my-2 overflow-x-auto rounded p-2"
><code class="hljs language-javascript"><span class="hljs-keyword">const</span> memoizedValue = <span class="hljs-title function_">useMemo</span>(<span class="hljs-function">() =></span> <span class="hljs-title function_">computeExpensiveValue</span>(a, b), [a, b]);
</code></pre>
<p class="bg-red-400 mb-[2px] mt-[2px]"><strong>When to Use:</strong></p>
<ul class="bg-limegreen my-[2px] list-disc pl-5">
<li class="bg-cornflowerblue my-[2px]">
<strong>Expensive Calculations:</strong> Use
<code class="rounded bg-black/5 px-1 text-[13px]">useMemo</code> when you have a calculation
that is computationally expensive and you want to avoid recalculating it on every render
unless its dependencies change.
</li>
<li class="bg-cornflowerblue my-[2px]">
<strong>Derived State:</strong> When deriving state from props or state that requires some
computation.
</li>
</ul>
</div>
I was experiencing this. The problem for me was that I had a container with whitespace: pre-wrap
. Removing this solved the spacing issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With