react-render-to-markdown
v19.0.1
Published
render React components to Markdown strings for SSG-MD
Downloads
1,560
Maintainers
Readme
react-render-to-markdown
Render React components to Markdown strings — like renderToString in react-dom, but outputs Markdown instead of HTML.
Built on top of react-reconciler, this library creates a custom React renderer that traverses the React element tree and produces well-formatted Markdown. It follows SSR-like behavior: useEffect, useLayoutEffect, and useInsertionEffect are suppressed (as no-ops), while useState, useMemo, useRef, useContext, and other synchronous hooks work as expected.
Installation
The major version of react-render-to-markdown follows the React version. Install the one that matches your project:
# React 19
npm install react-render-to-markdown@19
# React 18
npm install react-render-to-markdown@18Quick Start
import { renderToMarkdownString } from 'react-render-to-markdown';
const markdown = await renderToMarkdownString(<h1>Hello, World!</h1>);
console.log(markdown); // # Hello, World!Usage
Basic HTML Elements
import { renderToMarkdownString } from 'react-render-to-markdown';
await renderToMarkdownString(
<div>
<strong>foo</strong>
<span>bar</span>
</div>,
);
// Output: '**foo**bar'React Components & Hooks
Synchronous hooks (useState, useMemo, useRef, useContext, etc.) work as expected. Client-side effects (useEffect, useLayoutEffect) are automatically suppressed:
import { createContext, useContext, useMemo, useState } from 'react';
import { renderToMarkdownString } from 'react-render-to-markdown';
const ThemeContext = createContext('light');
const Article = () => {
const [count] = useState(42);
const theme = useContext(ThemeContext);
const doubled = useMemo(() => count * 2, [count]);
return (
<>
<h1>Hello World</h1>
<p>Count: {count}, Doubled: {doubled}, Theme: {theme}</p>
</>
);
};
await renderToMarkdownString(
<ThemeContext.Provider value="dark">
<Article />
</ThemeContext.Provider>,
);
// Output:
// # Hello World
//
// Count: 42, Doubled: 84, Theme: darkCode Blocks
Fenced code blocks with language and title support:
await renderToMarkdownString(
<pre data-lang="ts" data-title="rspress.config.ts">
<code>{'const a = 1;\n'}</code>
</pre>,
);
// Output:
// ```ts title=rspress.config.ts
// const a = 1;
// ```For languages that may contain triple backticks (like markdown, mdx, md), four backticks (``````) are automatically used as delimiters.
Supported Elements
| HTML Element | Markdown Output |
| --- | --- |
| <h1> – <h6> | # – ###### headings |
| <p> | Paragraph with trailing newlines |
| <strong>, <b> | **bold** |
| <em>, <i> | *italic* |
| <code> | `inline code` |
| <pre> + <code> | Fenced code block (```) |
| <a href=""> | [text](url) |
| <img> |  |
| <ul>, <ol>, <li> | Unordered / ordered lists |
| <blockquote> | > blockquote |
| <br> | Line break |
| <hr> | --- horizontal rule |
| <table>, <thead>, <tbody>, <tr>, <th>, <td> | GFM table |
Any unrecognized elements (e.g. <div>, <span>, <section>) render their children as-is, acting as transparent wrappers.
How It Works
- Custom React Reconciler — Uses
react-reconcilerto build a lightweight tree ofMarkdownNodeobjects from your React element tree. - SSR-like Hook Behavior — Client-side effects (
useEffect,useLayoutEffect,useInsertionEffect) are intercepted and turned into no-ops, matching React's Fizz server renderer behavior. This ensures browser-only code (e.g.document,window) in effects never runs. - Tree-to-Markdown Serialization — The
MarkdownNodetree is serialized to a Markdown string via a recursivetoMarkdownfunction.
Requirements
{
"react": ">=19.0.0",
"react-reconciler": "^0.33.0"
}Note: React 19 or above is required. The effect-interception mechanism relies on React 19's internal hooks dispatcher (
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H).
Used By
- Rspress SSG-MD — Rspress uses this library to power its SSG-MD (Static Site Generation to Markdown) feature. SSG-MD renders documentation pages as Markdown files instead of HTML, generating
llms.txtandllms-full.txtfor Generative Engine Optimization (GEO), enabling better accessibility for AI agents and large language models.
