Skip to main content
The Markdown component accepts a components prop that allows you to customize how different markdown elements are rendered. You can override any combination of block and inline node renderers.

Overview

import { Markdown } from "react-markdown-parser";

export function CustomArticle({ content }: { content: string }) {
  return (
    <Markdown
      content={content}
      components={{
        CodeBlock: ({ content, info }) => {
          // Custom code block renderer
          return (
            <pre data-lang={info}>
              <code>{content}</code>
            </pre>
          );
        },
        Link: ({ href, children }) => {
          // Custom link renderer
          return (
            <a href={href} rel="noreferrer">
              {children}
            </a>
          );
        },
      }}
    />
  );
}

Block node components

Block components render block-level markdown elements. Each receives specific props based on the markdown node type.

Table

Renders markdown tables with header and body cells.
head
object
required
Table header configuration
head.cells
array
required
Array of header cells
head.cells[].children
ReactNode
required
Rendered content of the header cell
head.cells[].align
'left' | 'right' | 'center' | undefined
Text alignment for the column
body
object
required
Table body configuration
body.rows
array
required
Array of table rows
body.rows[].cells
array
required
Array of cells in this row
body.rows[].cells[].children
ReactNode
required
Rendered content of the cell
body.rows[].cells[].align
'left' | 'right' | 'center' | undefined
Text alignment for the cell
Table: ({ head, body }) => (
  <table className="custom-table">
    <thead>
      <tr>
        {head.cells.map((cell, i) => (
          <th key={i} style={{ textAlign: cell.align }}>
            {cell.children}
          </th>
        ))}
      </tr>
    </thead>
    <tbody>
      {body.rows.map((row, i) => (
        <tr key={i}>
          {row.cells.map((cell, j) => (
            <td key={j} style={{ textAlign: cell.align }}>
              {cell.children}
            </td>
          ))}
        </tr>
      ))}
    </tbody>
  </table>
)

CodeBlock

Renders fenced code blocks.
content
string
required
The raw code content
info
string
The info string from the code fence (typically the language identifier)
CodeBlock: ({ content, info }) => {
  const language = info || 'text';
  return (
    <pre className={`language-${language}`}>
      <code>{content}</code>
    </pre>
  );
}

Blockquote

Renders blockquote elements.
children
ReactNode
required
The rendered child nodes (paragraphs, lists, etc.) inside the blockquote
Blockquote: ({ children }) => (
  <blockquote className="border-l-4 pl-4 italic">
    {children}
  </blockquote>
)

List

Renders ordered and unordered lists. The props differ based on list type. Ordered list props:
type
'ordered'
required
Indicates this is an ordered list
items
array
required
Array of list items
items[].children
ReactNode
required
Rendered content of the list item
start
number
The starting number for ordered lists (default: 1)
Unordered list props:
type
'unordered'
required
Indicates this is an unordered list
items
array
required
Array of list items
items[].children
ReactNode
required
Rendered content of the list item
List: (props) => {
  if (props.type === 'ordered') {
    return (
      <ol start={props.start} className="list-decimal">
        {props.items.map((item, i) => (
          <li key={i}>{item.children}</li>
        ))}
      </ol>
    );
  }
  return (
    <ul className="list-disc">
      {props.items.map((item, i) => (
        <li key={i}>{item.children}</li>
      ))}
    </ul>
  );
}

Heading

Renders headings from h1 to h6.
level
1 | 2 | 3 | 4 | 5 | 6
required
The heading level
children
ReactNode
required
The rendered heading text and inline elements
Heading: ({ level, children }) => {
  const id = slugify(children);
  const HeadingTag = `h${level}` as const;
  return (
    <HeadingTag id={id} className={`heading-${level}`}>
      {children}
    </HeadingTag>
  );
}

Paragraph

Renders paragraph elements.
children
ReactNode
required
The rendered inline content of the paragraph
Paragraph: ({ children }) => (
  <p className="my-4 leading-relaxed">
    {children}
  </p>
)

ThematicBreak

Renders horizontal rules (thematic breaks). This component receives no props.
ThematicBreak: () => (
  <hr className="my-8 border-gray-300" />
)

HtmlBlock

Renders raw HTML blocks.
content
string
required
The raw HTML content
Be cautious when rendering HTML blocks from untrusted sources. Consider sanitizing the content.
HtmlBlock: ({ content }) => (
  <div dangerouslySetInnerHTML={{ __html: sanitize(content) }} />
)

Inline node components

Inline components render inline-level markdown elements within block content.

Text

Renders plain text nodes.
text
string
required
The text content
Text: ({ text }) => <>{text}</>

CodeSpan

Renders inline code.
text
string
required
The code text
CodeSpan: ({ text }) => (
  <code className="bg-gray-100 px-1 py-0.5 rounded">
    {text}
  </code>
)

Emphasis

Renders emphasized (italic) text.
children
ReactNode
required
The rendered child inline nodes
Emphasis: ({ children }) => (
  <em className="italic">{children}</em>
)

Strong

Renders strong (bold) text.
children
ReactNode
required
The rendered child inline nodes
Strong: ({ children }) => (
  <strong className="font-bold">{children}</strong>
)
Renders hyperlinks.
href
string
required
The link destination URL
title
string
The optional link title attribute
children
ReactNode
required
The rendered link text
The default Link renderer includes URL validation to prevent XSS attacks. If you override this component, ensure you implement similar security measures.
Link: ({ href, title, children }) => {
  // Internal links use Next.js routing
  if (href.startsWith('/')) {
    return <NextLink href={href} title={title}>{children}</NextLink>;
  }
  // External links
  return (
    <a href={href} title={title} target="_blank" rel="noopener noreferrer">
      {children}
    </a>
  );
}

Image

Renders images.
href
string
required
The image source URL
alt
string
required
The image alt text (extracted from the markdown image text)
title
string
The optional image title attribute
The default Image renderer includes URL validation. Ensure custom implementations maintain security.
Image: ({ href, alt, title }) => (
  <img
    src={href}
    alt={alt}
    title={title}
    loading="lazy"
    className="max-w-full h-auto"
  />
)

HardBreak

Renders explicit line breaks. This component receives no props.
HardBreak: () => <br />

SoftBreak

Renders soft line breaks (typically converted to spaces). This component receives no props. The default renderer returns null.
SoftBreak: () => <>{' '}</>

Html

Renders inline HTML.
content
string
required
The raw HTML content
Be cautious when rendering inline HTML from untrusted sources. Consider sanitizing the content.
Html: ({ content }) => (
  <span dangerouslySetInnerHTML={{ __html: sanitize(content) }} />
)

Complete example

import { Markdown } from "react-markdown-parser";
import { highlight } from "highlight.js";

export function ArticleWithHighlighting({ content }: { content: string }) {
  return (
    <Markdown
      content={content}
      components={{
        CodeBlock: ({ content, info }) => {
          const language = info || "plaintext";
          const highlighted = highlight.highlight(content, { language });
          return (
            <pre className={`hljs language-${language}`}>
              <code dangerouslySetInnerHTML={{ __html: highlighted.value }} />
            </pre>
          );
        },
      }}
    />
  );
}

Type reference

type MarkdownComponents = BlockNodeComponents & InlineNodeComponents;

interface BlockNodeComponents {
  Table: ComponentType<{
    head: {
      cells: {
        children: ReactNode;
        align: "left" | "right" | "center" | undefined;
      }[];
    };
    body: {
      rows: {
        cells: {
          children: ReactNode;
          align: "left" | "right" | "center" | undefined;
        }[];
      }[];
    };
  }>;
  CodeBlock: ComponentType<{ content: string; info?: string }>;
  Blockquote: ComponentType<{ children: ReactNode }>;
  List: ComponentType<
    | { type: "ordered"; items: { children: ReactNode }[]; start?: number }
    | { type: "unordered"; items: { children: ReactNode }[] }
  >;
  Heading: ComponentType<{ level: 1 | 2 | 3 | 4 | 5 | 6; children: ReactNode }>;
  Paragraph: ComponentType<{ children: ReactNode }>;
  ThematicBreak: ComponentType;
  HtmlBlock: ComponentType<{ content: string }>;
}

interface InlineNodeComponents {
  Text: ComponentType<{ text: string }>;
  CodeSpan: ComponentType<{ text: string }>;
  Emphasis: ComponentType<{ children: ReactNode }>;
  Strong: ComponentType<{ children: ReactNode }>;
  Link: ComponentType<{ href: string; title?: string; children: ReactNode }>;
  Image: ComponentType<{ href: string; title?: string; alt: string }>;
  HardBreak: ComponentType;
  SoftBreak: ComponentType;
  Html: ComponentType<{ content: string }>;
}

Build docs developers (and LLMs) love