react-markdown

repository·main·Indexed 12 days ago

https://github.com/remarkjs/react-markdown

A React component that safely renders markdown to React elements using the unified ecosystem (remark and rehype). Version 10.1.0 provides synchronous rendering via the Markdown component, asynchronous support via MarkdownAsync for SSR, and MarkdownHooks for client-side async plugins. It is highly extensible through remarkPlugins, rehypePlugins, and a components prop for mapping markdown elements to custom React components.

Tokens
4.5K
Snippets
20
Records
27
Agent score
91%

What's inside react-markdown

  1. How react-markdown works (Architecture)

    main

    react-markdown is a unified pipeline that processes content through several stages:

    1. Parse: Markdown is parsed into an mdast (markdown syntax tree).
    2. Transform (Markdown): The mdast is transformed using remark plugins.
    3. Convert: The mdast is converted into a hast (HTML syntax tree) via remark-rehype.
    4. Transform (HTML): The hast is transformed using rehype plugins.
    5. Render: The hast is rendered into React elements using the components mapping.
  2. Customize rendered elements with the components prop

    main

    The components prop allows you to overwrite the default HTML elements generated from markdown. The keys in the components object are the HTML tags (e.g., h1, em, a).

    Each component receives standard HTML props plus a node prop, which is the original hast element. You can use this to implement custom styling or integrate third-party libraries like syntax highlighters.

    import Markdown from 'react-markdown'
    import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
    
    <Markdown
      components={{
        code(props) {
          const {children, className, node, ...rest} = props
          const match = /language-(\w+)/.exec(className || '')
          return match ? (
            <SyntaxHighlighter
              {...rest}
              PreTag="div"
              children={String(children).replace(/\n$/, '')}
              language={match[1]}
              style={dark}
            />
          ) : (
            <code {...rest} className={className}>
              {children}
            </code>
          )
        }
      }}
    >
      {markdown}
    </Markdown>
  3. Use rehype plugins to transform HTML

    main

    Use rehypePlugins to apply transformations to the HTML syntax tree (hast). A common use case is rendering math using remark-math for syntax extension and rehype-katex for HTML transformation.

    import Markdown from 'react-markdown'
    import rehypeKatex from 'rehype-katex'
    import remarkMath from 'remark-math'
    import 'katex/dist/katex.min.css'
    
    const markdown = `The lift coefficient ($C_L$) is a dimensionless coefficient.`
    
    <Markdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>
      {markdown}
    </Markdown>
  4. Use remark plugins to extend markdown syntax

    main

    You can extend markdown support (e.g., for tables, tasklists, or strikethrough) by passing plugins to the remarkPlugins prop. To pass options to a plugin, use an array where the first element is the plugin function and the second is the options object.

    import Markdown from 'react-markdown'
    import remarkGfm from 'remark-gfm'
    
    // Basic usage
    <Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
    
    // Usage with options
    <Markdown remarkPlugins={[[remarkGfm, {singleTilde: false}]]}>{markdown}</Markdown>
  5. Ensure security and prevent XSS

    main

    While react-markdown is secure by default, you can introduce vulnerabilities by:

    • Overwriting urlTransform with an insecure implementation.
    • Using insecure remarkPlugins, rehypePlugins, or components.

    To ensure content is completely safe, especially when using plugins, it is recommended to use rehype-sanitize. This allows you to define a custom schema of allowed elements and attributes.

  6. Enable HTML parsing in markdown with rehype-raw

    main

    By default, react-markdown escapes or ignores HTML for security. If you trust the source and need to render raw HTML within your markdown, use the rehype-raw plugin via the rehypePlugins prop.

    import Markdown from 'react-markdown'
    import rehypeRaw from 'rehype-raw'
    
    const markdown = `<div class="note">Some *emphasis*!</div>`
    
    <Markdown rehypePlugins={[rehypeRaw]}>
      {markdown}
    </Markdown>
  7. Handle line endings and indentation in JSX

    main

    When passing markdown to the Markdown component, avoid writing markdown directly inside JSX tags, as JSX collapses whitespace and line endings, which breaks markdown formatting.

    To ensure correct rendering, use one of the following patterns:

    1. Use a variable: Store your markdown in a variable and pass it as an expression. Do not indent the markdown content inside the variable.
    2. Use a template literal expression: Pass a template literal as an expression. Be careful with indentation inside template literals, as leading whitespace will be interpreted as an indented code block rather than markdown syntax (like headings).

    Avoid this (JSX collapses whitespace):

    <Markdown>
      # Hi
    </Markdown>

    Avoid this (Indentation creates code blocks):

    <Markdown>{`
        # This is an indented code block, not a heading
    `}</Markdown>
    // Recommended: Use a variable without indentation
    const markdown = `
    # This is perfect!
    `
    
    const result = <Markdown>{markdown}</Markdown>
    
    // Alternative: Use a template literal expression
    <Markdown>{`
    # Hi
    
    This is a paragraph.
    `}</Markdown>
  8. Use remark plugins with Markdown

    main

    You can extend markdown functionality (like GFM support) by passing plugins to the remarkPlugins prop. For example, using remark-gfm adds support for footnotes, strikethrough, tables, tasklists, and URLs.

    import React from 'react'
    import {createRoot} from 'react-dom/client'
    import Markdown from 'react-markdown'
    import remarkGfm from 'remark-gfm'
    
    const markdown = `Just a link: www.nasa.gov.`
    
    createRoot(document.body).render(
      <Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
    )
  9. Map markdown elements to custom components or tags

    main

    You can use the components prop to map markdown elements to different HTML tags or React components. For example, you can map an h1 to an h2 tag, or wrap an em tag in a styled i tag.

    <Markdown
      components={{
        // Map `h1` (`# heading`) to use `h2`s.
        h1: 'h2',
        // Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
        em(props) {
          const {node, ...rest} = props
          return <i style={{color: 'red'}} {...rest} />
        }
      }}
    />
  10. Basic usage of the Markdown component

    main

    The Markdown component is a synchronous component that renders a string of markdown into React elements. It is safe by default and does not use dangerouslySetInnerHTML.

    import React from 'react'
    import {createRoot} from 'react-dom/client'
    import Markdown from 'react-markdown'
    
    const markdown = '# Hi, *Pluto*!'
    
    createRoot(document.body).render(<Markdown>{markdown}</Markdown>)
  11. Configure `react-markdown` options

    main

    The Options object allows you to customize the rendering process. Key options include:

    • children: The markdown string to render.
    • components: A map of tag names to custom React components.
    • remarkPlugins: A list of remark plugins.
    • rehypePlugins: A list of rehype plugins.
    • remarkRehypeOptions: Options passed through to remark-rehype.
    • allowedElements: An array of tag names to allow (cannot be used with disallowedElements).
    • disallowedElements: An array of tag names to disallow (cannot be used with allowedElements).
    • allowElement: A function (element, index, parent) => boolean to filter elements.
    • skipHtml: If true, ignores HTML in markdown completely.
    • unwrapDisallowed: If true, extracts children from disallowed elements instead of dropping them.
    • urlTransform: A function to transform URLs (defaults to defaultUrlTransform).
    <Markdown
      children="# Hello"
      components={{
        h1: ({node, ...props}) => <h1 style={{color: 'red'}} {...props} />
      }}
      remarkPlugins={[somePlugin]}
      allowedElements={['p', 'strong']}
    />