npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

markdown-to-jsx

v9.3.0

Published

A very fast and versatile markdown toolchain. AST, React, React Native, SolidJS, Vue, Markdown, and HTML output available with full customization.

Readme

npm version downloads

markdown-to-jsx is a gfm+commonmark compliant markdown parser and compiler toolchain for JavaScript and TypeScript-based projects. It is extremely fast, capable of processing large documents fast enough for real-time interactivity.

Some special features of the library:

  • Arbitrary HTML is supported and parsed into the appropriate JSX representation without dangerouslySetInnerHTML

  • Any HTML tags rendered by the compiler and/or <Markdown> component can be overridden to include additional props or even a different HTML representation entirely.

  • All GFM special syntaxes are supported, including tables, task lists, strikethrough, autolinks, tag filtering, and more.

  • Fenced code blocks with highlight.js support; see Syntax highlighting for instructions on setting up highlight.js.

Upgrading

From v8.x to v9.x

Breaking Changes:

  • ast option removed: The ast: true option on compiler() has been removed. Use the new parser() function instead to access the AST directly.
/** v8 */ compiler('# Hello world', { ast: true })
/** v9 */ parser('# Hello world')
  • namedCodesToUnicode option removed: The namedCodesToUnicode option has been removed. All named HTML entities are now supported by default via the full entity list, so custom entity mappings are no longer needed.
/** v8 */ compiler('&le; symbol', { namedCodesToUnicode: { le: '\u2264' } })
/** v9 */ compiler('&le; symbol')
  • tagfilter enabled by default: Dangerous HTML tags (script, iframe, style, title, textarea, xmp, noembed, noframes, plaintext) are now escaped by default in both HTML string output and React JSX output. Previously these tags were rendered as JSX elements in React output.
/** v8 */ tags rendered as JSX elements
/** v9 */ tags escaped by default
compiler('<script>alert("xss")</script>') // <span>&lt;script&gt;</span>

/** Restore old behavior */
compiler('<script>alert("xss")</script>', { tagfilter: false })

New Features:

  • New parser function: Provides direct access to the parsed AST without rendering. This is the recommended way to get AST nodes.

  • New entry points: React-specific, HTML-specific, and markdown-specific entry points are now available for better tree-shaking and separation of concerns.

// React-specific usage
import Markdown, { compiler, parser } from 'markdown-to-jsx/react'

// HTML string output
import { compiler, astToHTML, parser } from 'markdown-to-jsx/html'

// Markdown string output (round-trip compilation)
import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'

Migration Guide:

  1. Replace compiler(..., { ast: true }) with parser():
/** v8 */ compiler(markdown, { ast: true })
/** v9 */ parser(markdown)
  1. Migrate React imports to /react entry point (optional but recommended):
/** Legacy */ import from 'markdown-to-jsx'
/** Recommended */ import from 'markdown-to-jsx/react'
  1. Remove namedCodesToUnicode option: All named HTML entities are now supported automatically, so you can remove any custom entity mappings.
/** v8 */ compiler('&le; symbol', { namedCodesToUnicode: { le: '\u2264' } })
/** v9 */ compiler('&le; symbol')

Note: The main entry point (markdown-to-jsx) continues to work for backward compatibility, but React code there is deprecated and will be removed in a future major release. Consider migrating to markdown-to-jsx/react for React-specific usage.

From v7.x to v8.x

Breaking Changes:

  • Type ParserResult renamed to ASTNode - If you were using MarkdownToJSX.ParserResult in your code, update to MarkdownToJSX.ASTNode
/** v7 */ MarkdownToJSX.ParserResult[]
/** v8+ */ MarkdownToJSX.ASTNode[]
  • Multiple RuleType enums consolidated into RuleType.textFormatted - If you were checking for RuleType.textBolded, RuleType.textEmphasized, RuleType.textMarked, or RuleType.textStrikethroughed, update to check for RuleType.textFormatted and inspect the node's boolean flags:
/** v7 */ RuleType.textBolded
/** v8+ */ RuleType.textFormatted && node.bold

Installation

Install markdown-to-jsx with your favorite package manager.

npm i markdown-to-jsx

Usage

markdown-to-jsx exports a React component by default for easy JSX composition:

ES6-style usage*:

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

render(<Markdown># Hello world!</Markdown>, document.body)

/*
    renders:

    <h1>Hello world!</h1>
 */

* NOTE: JSX does not natively preserve newlines in multiline text. In general, writing markdown directly in JSX is discouraged and it's a better idea to keep your content in separate .md files and require them, perhaps using webpack's raw-loader.

Entry Points

markdown-to-jsx provides multiple entry points for different use cases:

Main

The legacy*default entry point exports everything, including the React compiler and component:

import Markdown, { compiler, parser } from 'markdown-to-jsx'

The React code in this entry point is deprecated and will be removed in a future major release, migrate to markdown-to-jsx/react.

React

For React-specific usage, import from the /react entry point:

import Markdown, { compiler, parser, astToJSX } from 'markdown-to-jsx/react'

const jsxElement = compiler('# Hello world')

function App() {
  return <Markdown children="# Hello world" />
}

/** Or use parser + astToJSX */
const ast = parser('# Hello world')
const jsxElement2 = astToJSX(ast)

React Native

For React Native usage, import from the /native entry point:

import Markdown, { compiler, parser, astToNative } from 'markdown-to-jsx/native'
import { View, Text, StyleSheet, Linking } from 'react-native'

const nativeElement = compiler('# Hello world', {
  styles: {
    heading1: { fontSize: 32, fontWeight: 'bold' },
    paragraph: { marginVertical: 8 },
    link: { color: 'blue', textDecorationLine: 'underline' },
  },
  onLinkPress: url => {
    Linking.openURL(url)
  },
})

const markdown = `# Hello world

This is a [link](https://example.com) with **bold** and *italic* text.
`

function App() {
  return (
    <View>
      <Markdown
        children={markdown}
        options={{
          styles: StyleSheet.create({
            heading1: { fontSize: 32, fontWeight: 'bold' },
            paragraph: { marginVertical: 8 },
            link: { color: 'blue', textDecorationLine: 'underline' },
          }),
          onLinkPress: url => {
            Linking.openURL(url)
          },
        }}
      />
    </View>
  )
}

React Native-specific options:

  • onLinkPress?: (url: string, title?: string) => void - Custom handler for link presses (defaults to Linking.openURL)
  • onLinkLongPress?: (url: string, title?: string) => void - Handler for link long presses
  • styles?: Partial<Record<NativeStyleKey, StyleProp<ViewStyle | TextStyle | ImageStyle>>> - Style overrides for each element type
  • wrapperProps?: ViewProps | TextProps - Props for the wrapper component (defaults to View for block, Text for inline)

HTML Tag Mapping: HTML tags are automatically mapped to React Native components:

  • <img>Image component
  • Block elements (<div>, <section>, <article>, <blockquote>, <ul>, <ol>, <li>, <table>, etc.) → View component
  • Inline elements (<span>, <strong>, <em>, <a>, etc.) → Text component
  • Type 1 blocks (<pre>, <script>, <style>, <textarea>) → View component

Note: Links are underlined by default for better accessibility and discoverability. You can override this via the styles.link option.

SolidJS

For SolidJS usage, import from the /solid entry point:

import Markdown, {
  compiler,
  parser,
  astToJSX,
  MarkdownProvider,
} from 'markdown-to-jsx/solid'
import { createSignal } from 'solid-js'

// Static content
const solidElement = compiler('# Hello world')

function App() {
  return <Markdown children="# Hello world" />
}

// Reactive content (automatically updates when content changes)
function ReactiveApp() {
  const [content, setContent] = createSignal('# Hello world')
  return <Markdown>{content}</Markdown>
}

// Or use parser + astToJSX
const ast = parser('# Hello world')
const solidElement2 = astToJSX(ast)

// Use context for default options
function AppWithContext() {
  return (
    <MarkdownProvider options={{ sanitizer: customSanitizer }}>
      <Markdown># Content</Markdown>
    </MarkdownProvider>
  )
}

SolidJS-specific features:

  • Reactive content: The Markdown component accepts signals/accessors for automatic updates when markdown content changes
  • Memoization: AST parsing is automatically memoized for optimal performance
  • Context API: Use MarkdownProvider to provide default options and avoid prop drilling

Vue.js

For Vue.js 3 usage, import from the /vue entry point:

import Markdown, { compiler, parser, astToJSX } from 'markdown-to-jsx/vue'
import { h } from 'vue'

// Using compiler
const vnode = compiler('# Hello world')

// Using component
<Markdown children="# Hello world" />

// Or use parser + astToJSX
const ast = parser('# Hello world')
const vnode2 = astToJSX(ast)

Vue.js-specific features:

  • Vue 3 support: Uses Vue 3's h() render function API
  • JSX support: Works with Vue 3 JSX via @vue/babel-plugin-jsx or @vitejs/plugin-vue-jsx
  • HTML attributes: Uses standard HTML attributes (class instead of className)
  • Component overrides: Support for both Options API and Composition API componen

HTML

For HTML string output (server-side rendering), import from the /html entry point:

import { compiler, html, parser } from 'markdown-to-jsx/html'

const htmlString = compiler('# Hello world')

/** Or use parser + html */
const ast = parser('# Hello world')
const htmlString2 = html(ast)

Markdown

For markdown-to-markdown compilation (normalization and formatting), import from the /markdown entry point:

import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'

const normalizedMarkdown = compiler('# Hello  world\n\nExtra spaces!')

/** Or work with AST */
const ast = parser('# Hello  world')
const normalizedMarkdown2 = astToMarkdown(ast)

Library Options

All Options

| Option | Type | Default | Description | | ----------------------- | ----------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | | createElement | function | - | Custom createElement behavior (React/React Native/SolidJS/Vue only). See createElement for details. | | disableAutoLink | boolean | false | Disable automatic conversion of bare URLs to anchor tags. | | disableParsingRawHTML | boolean | false | Disable parsing of raw HTML into JSX. | | enforceAtxHeadings | boolean | false | Require space between # and header text (GFM spec compliance). | | forceBlock | boolean | false | Force all content to be treated as block-level. | | forceInline | boolean | false | Force all content to be treated as inline. | | forceWrapper | boolean | false | Force wrapper even with single child (React/React Native/Vue only). See forceWrapper for details. | | overrides | object | - | Override HTML tag rendering. See overrides for details. | | preserveFrontmatter | boolean | false | Include frontmatter in rendered output (as <pre> for HTML/JSX, included in markdown). Behavior varies by compiler type. | | renderRule | function | - | Custom rendering for AST rules. See renderRule for details. | | sanitizer | function | built-in | Custom URL sanitizer function. See sanitizer for details. | | slugify | function | built-in | Custom slug generation for heading IDs. See slugify for details. | | tagfilter | boolean | true | Escape dangerous HTML tags (script, iframe, style, etc.) to prevent XSS. | | wrapper | string \| component \| null | 'div' | Wrapper element for multiple children (React/React Native/Vue only). See wrapper for details. | | wrapperProps | object | - | Props for wrapper element (React/React Native/Vue only). See wrapperProps for details. |

options.createElement

Sometimes, you might want to override the React.createElement default behavior to hook into the rendering process before the JSX gets rendered. This might be useful to add extra children or modify some props based on runtime conditions. The function mirrors the React.createElement function, so the params are type, [props], [...children]:

import Markdown from 'markdown-to-jsx'
import React from 'react'
import { render } from 'react-dom'

const md = `
# Hello world
`

render(
  <Markdown
    children={md}
    options={{
      createElement(type, props, children) {
        return (
          <div className="parent">
            {React.createElement(type, props, children)}
          </div>
        )
      },
    }}
  />,
  document.body
)

options.forceWrapper

By default, the compiler does not wrap the rendered contents if there is only a single child. You can change this by setting forceWrapper to true. If the child is inline, it will not necessarily be wrapped in a span.

// Using `forceWrapper` with a single, inline child…
<Markdown options={{ wrapper: 'aside', forceWrapper: true }}>
  Mumble, mumble…
</Markdown>

// renders

<aside>Mumble, mumble…</aside>

options.overrides

Override HTML tag rendering or render custom React components. Three use cases:

1. Remove tags: Return null to completely remove tags (beyond tagfilter escaping):

<Markdown options={{ overrides: { iframe: () => null } }}>
  <iframe src="..."></iframe>
</Markdown>

2. Override HTML tags: Change component, props, or both:

const MyParagraph = ({ children, ...props }) => <div {...props}>{children}</div>

<Markdown options={{ overrides: { h1: { component: MyParagraph, props: { className: 'foo' } } } }}>
  # Hello
</Markdown>

/** Simplified */ { overrides: { h1: MyParagraph } }

3. Render React components: Use custom components in markdown:

import DatePicker from './date-picker'

const md = `<DatePicker timezone="UTC+5" startTime={1514579720511} />`

<Markdown options={{ overrides: { DatePicker } }}>{md}</Markdown>

Important notes:

  • Props are passed as strings; parse them in your component (e.g., JSON.parse(columns))
  • JSX interpolations ({value}) are passed as raw strings
  • Some props are preserved: a (href, title), img (src, alt, title), input[type="checkbox"] (checked, readonly), ol (start), td/th (style)
  • Element mappings: span for inline text, code for inline code, pre > code for code blocks

options.renderRule

Supply your own rendering function that can selectively override how rules are rendered (note, this is different than options.overrides which operates at the HTML tag level and is more general). You can use this functionality to do pretty much anything with an established AST node; here's an example of selectively overriding the "codeBlock" rule to process LaTeX syntax using the @matejmazur/react-katex library:

import Markdown, { RuleType } from 'markdown-to-jsx'
import TeX from '@matejmazur/react-katex'

const exampleContent =
  'Some important formula:\n\n```latex\nmathbb{N} = { a in mathbb{Z} : a > 0 }\n```\n'

function App() {
  return (
    <Markdown
      children={exampleContent}
      options={{
        renderRule(next, node, renderChildren, state) {
          if (node.type === RuleType.codeBlock && node.lang === 'latex') {
            return (
              <TeX as="div" key={state.key}>{String.raw`${node.text}`}</TeX>
            )
          }

          return next()
        },
      }}
    />
  )
}

options.sanitizer

By default a lightweight URL sanitizer function is provided to avoid common attack vectors that might be placed into the href of an anchor tag, for example. The sanitizer receives the input, the HTML tag being targeted, and the attribute name. The original function is available as a library export called sanitizer.

This can be overridden and replaced with a custom sanitizer if desired via options.sanitizer:

// sanitizer in this situation would receive:
// ('javascript:alert("foo")', 'a', 'href')

;<Markdown options={{ sanitizer: (value, tag, attribute) => value }}>
  {`[foo](javascript:alert("foo"))`}
</Markdown>

// or

compiler('[foo](javascript:alert("foo"))', {
  sanitizer: value => value,
})

options.slugify

By default, a lightweight deburring function is used to generate an HTML id from headings. You can override this by passing a function to options.slugify. This is helpful when you are using non-alphanumeric characters (e.g. Chinese or Japanese characters) in headings. For example:

;<Markdown options={{ slugify: str => str }}># 中文</Markdown>
compiler('# 中文', { slugify: str => str })

The original function is available as a library export called slugify.

options.wrapper

When there are multiple children to be rendered, the compiler will wrap the output in a div by default. You can override this default by setting the wrapper option to either a string (React Element) or a component.

const str = '# Heck Yes\n\nThis is great!'

<Markdown options={{ wrapper: 'article' }}>{str}</Markdown>

compiler(str, { wrapper: 'article' })
Other useful recipes

To get an array of children back without a wrapper, set wrapper to null. This is particularly useful when using compiler(…) directly.

compiler('One\n\nTwo\n\nThree', { wrapper: null })[
  /** Returns */ ((<p>One</p>), (<p>Two</p>), (<p>Three</p>))
]

To render children at the same DOM level as <Markdown> with no HTML wrapper, set wrapper to React.Fragment. This will still wrap your children in a React node for the purposes of rendering, but the wrapper element won't show up in the DOM.

options.wrapperProps

Props to apply to the wrapper element when wrapper is used.

<Markdown
  options={{
    wrapper: 'article',
    wrapperProps: { className: 'post', 'data-testid': 'markdown-content' },
  }}
>
  # Hello World
</Markdown>

Syntax highlighting

When using fenced code blocks with language annotation, that language will be added to the <code> element as class="lang-${language}". For best results, you can use options.overrides to provide an appropriate syntax highlighting integration like this one using highlight.js:

<!-- Add the following tags to your page <head> to automatically load hljs and styles: -->
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/obsidian.min.css"
/>

<script
  crossorigin
  src="https://unpkg.com/@highlightjs/[email protected]/highlight.min.js"
></script>
import { Markdown, RuleType } from 'markdown-to-jsx'

const mdContainingFencedCodeBlock = '```js\nconsole.log("Hello world!");\n```\n'

function App() {
  return (
    <Markdown
      children={mdContainingFencedCodeBlock}
      options={{
        overrides: {
          code: SyntaxHighlightedCode,
        },
      }}
    />
  )
}

function SyntaxHighlightedCode(props) {
  const ref = React.useRef<HTMLElement | null>(null)

  React.useEffect(() => {
    if (ref.current && props.className?.includes('lang-') && window.hljs) {
      window.hljs.highlightElement(ref.current)

      // hljs won't reprocess the element unless this attribute is removed
      ref.current.removeAttribute('data-highlighted')
    }
  }, [props.className, props.children])

  return <code {...props} ref={ref} />
}

Handling shortcodes

For Slack-style messaging with arbitrary shortcodes like :smile:, you can use options.renderRule to hook into the plain text rendering and adjust things to your liking, for example:

import Markdown, { RuleType } from 'markdown-to-jsx'

const shortcodeMap = {
  smile: '🙂',
}

const detector = /(:[^:]+:)/g

const replaceEmoji = (text: string): React.ReactNode => {
  return text.split(detector).map((part, index) => {
    if (part.startsWith(':') && part.endsWith(':')) {
      const shortcode = part.slice(1, -1)

      return <span key={index}>{shortcodeMap[shortcode] || part}</span>
    }

    return part
  })
}

function Example() {
  return (
    <Markdown
      options={{
        renderRule(next, node) {
          if (node.type === RuleType.text && detector.test(node.text)) {
            return replaceEmoji(node.text)
          }

          return next()
        },
      }}
    >
      {`On a beautiful summer day, all I want to do is :smile:.`}
    </Markdown>
  )
}

When you use options.renderRule, any React-renderable JSX may be returned including images and GIFs. Ensure you benchmark your solution as the text rule is one of the hottest paths in the system!

Usage with Preact

Everything will work just fine! Simply Alias react to preact/compat like you probably already are doing.

AST Anatomy

The Abstract Syntax Tree (AST) is a structured representation of parsed markdown. Each node in the AST has a type property that identifies its kind, and type-specific properties.

Important: The first node in the AST is typically a RuleType.refCollection node that contains all reference definitions found in the document, including footnotes (stored with keys prefixed with ^). This node is skipped during rendering but is useful for accessing reference data. Footnotes are automatically extracted from the refCollection and rendered in a <footer> element by both compiler() and astToJSX().

Node Types

The AST consists of the following node types (use RuleType to check node types):

Block-level nodes:

  • RuleType.heading - Headings (# Heading)
    { type: RuleType.heading, level: 1, id: "heading", children: [...] }
  • RuleType.paragraph - Paragraphs
    { type: RuleType.paragraph, children: [...] }
  • RuleType.codeBlock - Fenced code blocks (```)
    { type: RuleType.codeBlock, lang: "javascript", text: "code content" }
  • RuleType.blockQuote - Blockquotes (>)
    { type: RuleType.blockQuote, children: [...], alert?: "note" }
  • RuleType.orderedList / RuleType.unorderedList - Lists
    { type: RuleType.orderedList, items: [[...]], start?: 1 }
    { type: RuleType.unorderedList, items: [[...]] }
  • RuleType.table - Tables
    { type: RuleType.table, header: [...], cells: [[...]], align: [...] }
  • RuleType.htmlBlock - HTML blocks
    { type: RuleType.htmlBlock, tag: "div", attrs: {}, children: [...] }

Inline nodes:

  • RuleType.text - Plain text
    { type: RuleType.text, text: "Hello world" }
  • RuleType.textFormatted - Bold, italic, etc.
    { type: RuleType.textFormatted, tag: "strong", children: [...] }
  • RuleType.codeInline - Inline code (`)
    { type: RuleType.codeInline, text: "code" }
  • RuleType.link - Links
    { type: RuleType.link, target: "https://example.com", children: [...] }
  • RuleType.image - Images
    { type: RuleType.image, target: "image.png", alt: "description" }

Other nodes:

  • RuleType.breakLine - Hard line breaks ( )
  • RuleType.breakThematic - Horizontal rules (---)
  • RuleType.gfmTask - GFM task list items (- [ ])
  • RuleType.ref - Reference definition node (not rendered, stored in refCollection)
  • RuleType.refCollection - Reference definitions collection (appears at AST root, includes footnotes with ^ prefix)
  • RuleType.footnote - Footnote definition node (not rendered, stored in refCollection)
  • RuleType.footnoteReference - Footnote reference ([^identifier])
  • RuleType.frontmatter - YAML frontmatter blocks
    { type: RuleType.frontmatter, text: "---\ntitle: My Title\n---" }
  • RuleType.htmlComment - HTML comment nodes
    { type: RuleType.htmlComment, text: "<!-- comment -->" }
  • RuleType.htmlSelfClosing - Self-closing HTML tags
    { type: RuleType.htmlSelfClosing, tag: "img", attrs: { src: "image.png" } }

Example AST Structure

import { parser, RuleType } from 'markdown-to-jsx'

const ast = parser(`# Hello World

This is a **paragraph** with [a link](https://example.com).

[linkref]: https://example.com

```javascript
console.log('code')
```

`)

// AST structure:
[
  // Reference collection (first node, if references exist)
  {
    type: RuleType.refCollection,
    refs: {
      linkref: { target: 'https://example.com', title: undefined },
    },
  },
  {
    type: RuleType.heading,
    level: 1,
    id: 'hello-world',
    children: [{ type: RuleType.text, text: 'Hello World' }],
  },
  {
    type: RuleType.paragraph,
    children: [
      { type: RuleType.text, text: 'This is a ' },
      {
        type: RuleType.textFormatted,
        tag: 'strong',
        children: [{ type: RuleType.text, text: 'paragraph' }],
      },
      { type: RuleType.text, text: ' with ' },
      {
        type: RuleType.link,
        target: 'https://example.com',
        children: [{ type: RuleType.text, text: 'a link' }],
      },
      { type: RuleType.text, text: '.' },
    ],
  },
  {
    type: RuleType.codeBlock,
    lang: 'javascript',
    text: "console.log('code')",
  },
]

Type Checking

Use the RuleType enum to identify AST nodes:

import { RuleType } from 'markdown-to-jsx'

if (node.type === RuleType.heading) {
  const heading = node as MarkdownToJSX.HeadingNode
  console.log(`Heading level ${heading.level}: ${heading.id}`)
}

When to use compiler vs parser vs <Markdown>:

  • Use <Markdown> when you need a simple React component that renders markdown to JSX.
  • Use compiler when you need React JSX output from markdown (the component uses this internally).
  • Use parser + astToJSX when you need the AST for custom processing before rendering to JSX, or just the AST itself.

Gotchas

Props are stringified: When using overrides with React components, props are passed as strings. Parse them in your component:

const Table = ({ columns, dataSource, ...props }) => {
  const parsedColumns = JSON.parse(columns)
  const parsedData = JSON.parse(dataSource)
  // ... use parsed data
}

HTML indentation: Leading whitespace in HTML blocks is auto-trimmed based on the first line's indentation to avoid markdown syntax conflicts.

Code in HTML: Don't put code directly in HTML divs. Use fenced code blocks instead:

<div>
```js
var code = here();
```
</div>

Changelog

See Github Releases.

Donate

Like this library? It's developed entirely on a volunteer basis; chip in a few bucks if you can via the Sponsor link!