Back to Home
Published: Fri Jan 30 2026EN
#mdx#react#next.js#typescript#html

Rendering HTML Details/Summary in MDX with Custom Components

When working with MDX in Next.js projects, you might encounter a common challenge: HTML <details> and <summary> elements don't render properly. This guide explains the problem and provides a complete solution using custom React components.

Table of Contents

The Problem

MDX compiles content into JSX, but it doesn't natively support raw HTML elements like <details> and <summary>. When you try to use these elements in your MDX content:

HTML

You'll likely encounter one of these issues:

  1. Compilation Error: MDX throws an error because it can't process raw HTML nodes
  2. No Rendering: The elements are ignored or rendered as plain text
  3. rehype-raw Conflict: Using rehype-raw plugin causes conflicts with MDX JSX nodes

The error message typically looks like:

Plain Text

Why rehype-raw Doesn't Work

You might think adding rehype-raw to your MDX configuration would solve this:

TYPESCRIPT

The rehype-raw plugin is designed for regular markdown, not MDX. MDX has its own JSX handling that conflicts with raw HTML processing.

The Solution

The solution involves three parts:

  1. Create custom React components to handle the rendering
  2. Transform HTML tags to JSX components before MDX compilation
  3. Register components in the MDX component mapping

Step 1: Create Custom Components

Create Details and Summary components. Here's a basic implementation:

TSX

Add some basic styles:

CSS

Step 2: Transform HTML Tags Before Compilation

The key insight is that we need to transform HTML tags before MDX compiles them. We also need to be careful not to transform tags inside code blocks or inline code. Create a context-aware transform function:

TSX

This refined transformation:

  • Identifies code regions using regex to prevent accidental transformation within examples.
  • Converts <details> to <Details> only in prose.
  • Converts </details> to </Details> only in prose.
  • Converts <summary> to <Summary> only in prose.
  • Converts </summary> to </Summary> only in prose.

Step 3: Register Components in MDX

Finally, register the components in your MDX component mapping:

TSX

Why First Child as Summary?

In the HTML specification, <details> element's first child should always be <summary>. The component leverages this by:

  1. Taking the first child's content as the collapsible header
  2. Putting all remaining children in the collapsible body

This approach is more reliable than trying to identify the Summary component by type checking, which can fail due to how MDX compiles components.

Handling the open Attribute

HTML <details> supports an open attribute to show content by default:

HTML

The component handles this with an initial state:

TSX

Extending the Solution

You can extend this pattern for other HTML elements that MDX doesn't handle well by adding more replacements inside the non-code part of your transform function:

TYPESCRIPT

Summary

When MDX doesn't support certain HTML elements:

  1. Don't use rehype-raw - it conflicts with MDX's JSX processing
  2. Transform tags before compilation - convert HTML tags to JSX component names using string replacement
  3. Create custom React components - implement the desired behavior with your preferred UI approach
  4. Register in component mapping - make components available to MDX

This pattern maintains compatibility with remote or external MDX content while providing full control over how elements render.

Previous Payload Docker Config: PostgreSQL + BunJS
Next TypeSpec for .NET and Next.js Teams
An unhandled error has occurred. Reload