Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there excessive spacing between elements in ReactMarkdown rendering?

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):

Why are these so far apart!


What I’m using:

  • Next.js 15
  • React 19
  • ReactMarkdown react-markdown@9
  • Tailwind CSS v3
  • Plugins:
    • remark-gfm
    • rehype-highlight

Expected result:

Minimal vertical spacing between elements in the chat bubbles.

Actual result:

Headings and their following content have unwanted gaps that don’t collapse.


Code (ChatPage.tsx)

<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>

Tried so far:

  • Replacing all headings with <div> tags
  • Zeroing margins with mb-0, mt-0
  • Reducing adjacent spacing with [+ *] in Tailwind
  • Inspecting computed styles — no unexpected margin

Question:

  • Is 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.

Example of what is rendered in the DOM

<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">() =&gt;</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>

like image 734
Flavio Avatar asked Oct 16 '25 13:10

Flavio


1 Answers

I was experiencing this. The problem for me was that I had a container with whitespace: pre-wrap. Removing this solved the spacing issue.

like image 186
Finnian Langham Avatar answered Oct 19 '25 07:10

Finnian Langham