UNPKG

25 kBMarkdownView Raw
1<!--
2Notes for maintaining this document:
3
4* Update the link for `cm-html` once in a while
5-->
6
7# react-markdown
8
9[![Build][build-badge]][build]
10[![Coverage][coverage-badge]][coverage]
11[![Downloads][downloads-badge]][downloads]
12[![Size][size-badge]][size]
13[![Sponsors][sponsors-badge]][collective]
14[![Backers][backers-badge]][collective]
15[![Chat][chat-badge]][chat]
16
17React component to render markdown.
18
19## Feature highlights
20
21* [x] **[safe][section-security] by default**
22 (no `dangerouslySetInnerHTML` or XSS attacks)
23* [x] **[components][section-components]**
24 (pass your own component to use instead of `<h2>` for `## hi`)
25* [x] **[plugins][section-plugins]**
26 (many plugins you can pick and choose from)
27* [x] **[compliant][section-syntax]**
28 (100% to CommonMark, 100% to GFM with a plugin)
29
30## Contents
31
32* [What is this?](#what-is-this)
33* [When should I use this?](#when-should-i-use-this)
34* [Install](#install)
35* [Use](#use)
36* [API](#api)
37 * [`Markdown`](#markdown)
38 * [`defaultUrlTransform(url)`](#defaulturltransformurl)
39 * [`AllowElement`](#allowelement)
40 * [`Components`](#components)
41 * [`ExtraProps`](#extraprops)
42 * [`Options`](#options)
43 * [`UrlTransform`](#urltransform)
44* [Examples](#examples)
45 * [Use a plugin](#use-a-plugin)
46 * [Use a plugin with options](#use-a-plugin-with-options)
47 * [Use custom components (syntax highlight)](#use-custom-components-syntax-highlight)
48 * [Use remark and rehype plugins (math)](#use-remark-and-rehype-plugins-math)
49* [Plugins](#plugins)
50* [Syntax](#syntax)
51* [Types](#types)
52* [Compatibility](#compatibility)
53* [Architecture](#architecture)
54* [Appendix A: HTML in markdown](#appendix-a-html-in-markdown)
55* [Appendix B: Components](#appendix-b-components)
56* [Appendix C: line endings in markdown (and JSX)](#appendix-c-line-endings-in-markdown-and-jsx)
57* [Security](#security)
58* [Related](#related)
59* [Contribute](#contribute)
60* [License](#license)
61
62## What is this?
63
64This package is a [React][] component that can be given a string of markdown
65that it’ll safely render to React elements.
66You can pass plugins to change how markdown is transformed and pass components
67that will be used instead of normal HTML elements.
68
69* to learn markdown, see this [cheatsheet and tutorial][commonmark-help]
70* to try out `react-markdown`, see [our demo][demo]
71
72## When should I use this?
73
74There are other ways to use markdown in React out there so why use this one?
75The three main reasons are that they often rely on `dangerouslySetInnerHTML`,
76have bugs with how they handle markdown, or don’t let you swap elements for
77components.
78`react-markdown` builds a virtual DOM, so React only replaces what changed,
79from a syntax tree.
80That’s supported because we use [unified][], specifically [remark][] for
81markdown and [rehype][] for HTML, which are popular tools to transform content
82with plugins.
83
84This package focusses on making it easy for beginners to safely use markdown in
85React.
86When you’re familiar with unified, you can use a modern hooks based alternative
87[`react-remark`][react-remark] or [`rehype-react`][rehype-react] manually.
88If you instead want to use JavaScript and JSX *inside* markdown files, use
89[MDX][].
90
91## Install
92
93This package is [ESM only][esm].
94In Node.js (version 16+), install with [npm][]:
95
96```sh
97npm install react-markdown
98```
99
100In Deno with [`esm.sh`][esmsh]:
101
102```js
103import Markdown from 'https://esm.sh/react-markdown@9'
104```
105
106In browsers with [`esm.sh`][esmsh]:
107
108```html
109<script type="module">
110 import Markdown from 'https://esm.sh/react-markdown@9?bundle'
111</script>
112```
113
114## Use
115
116A basic hello world:
117
118```jsx
119import React from 'react'
120import {createRoot} from 'react-dom/client'
121import Markdown from 'react-markdown'
122
123const markdown = '# Hi, *Pluto*!'
124
125createRoot(document.body).render(<Markdown>{markdown}</Markdown>)
126```
127
128<details>
129<summary>Show equivalent JSX</summary>
130
131```jsx
132<h1>
133 Hi, <em>Pluto</em>!
134</h1>
135```
136
137</details>
138
139Here is an example that shows how to use a plugin ([`remark-gfm`][remark-gfm],
140which adds support for footnotes, strikethrough, tables, tasklists and URLs
141directly):
142
143```jsx
144import React from 'react'
145import {createRoot} from 'react-dom/client'
146import Markdown from 'react-markdown'
147import remarkGfm from 'remark-gfm'
148
149const markdown = `Just a link: www.nasa.gov.`
150
151createRoot(document.body).render(
152 <Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
153)
154```
155
156<details>
157<summary>Show equivalent JSX</summary>
158
159```jsx
160<p>
161 Just a link: <a href="http://www.nasa.gov">www.nasa.gov</a>.
162</p>
163```
164
165</details>
166
167## API
168
169This package exports the following identifier:
170[`defaultUrlTransform`][api-default-url-transform].
171The default export is [`Markdown`][api-markdown].
172
173### `Markdown`
174
175Component to render markdown.
176
177###### Parameters
178
179* `options` ([`Options`][api-options])
180 — props
181
182###### Returns
183
184React element (`JSX.Element`).
185
186### `defaultUrlTransform(url)`
187
188Make a URL safe.
189
190###### Parameters
191
192* `url` (`string`)
193 — URL
194
195###### Returns
196
197Safe URL (`string`).
198
199### `AllowElement`
200
201Filter elements (TypeScript type).
202
203###### Parameters
204
205* `node` ([`Element` from `hast`][hast-element])
206 — element to check
207* `index` (`number | undefined`)
208 — index of `element` in `parent`
209* `parent` ([`Node` from `hast`][hast-node])
210 — parent of `element`
211
212###### Returns
213
214Whether to allow `element` (`boolean`, optional).
215
216### `Components`
217
218Map tag names to components (TypeScript type).
219
220###### Type
221
222```ts
223import type {Element} from 'hast'
224
225type Components = Partial<{
226 [TagName in keyof JSX.IntrinsicElements]:
227 // Class component:
228 | (new (props: JSX.IntrinsicElements[TagName] & ExtraProps) => JSX.ElementClass)
229 // Function component:
230 | ((props: JSX.IntrinsicElements[TagName] & ExtraProps) => JSX.Element | string | null | undefined)
231 // Tag name:
232 | keyof JSX.IntrinsicElements
233}>
234```
235
236### `ExtraProps`
237
238Extra fields we pass to components (TypeScript type).
239
240###### Fields
241
242* `node` ([`Element` from `hast`][hast-element], optional)
243 — original node
244
245### `Options`
246
247Configuration (TypeScript type).
248
249###### Fields
250
251* `allowElement` ([`AllowElement`][api-allow-element], optional)
252 — filter elements;
253 `allowedElements` / `disallowedElements` is used first
254* `allowedElements` (`Array<string>`, default: all tag names)
255 — tag names to allow;
256 cannot combine w/ `disallowedElements`
257* `children` (`string`, optional)
258 — markdown
259* `className` (`string`, optional)
260 — wrap in a `div` with this class name
261* `components` ([`Components`][api-components], optional)
262 — map tag names to components
263* `disallowedElements` (`Array<string>`, default: `[]`)
264 — tag names to disallow;
265 cannot combine w/ `allowedElements`
266* `rehypePlugins` (`Array<Plugin>`, optional)
267 — list of [rehype plugins][rehype-plugins] to use
268* `remarkPlugins` (`Array<Plugin>`, optional)
269 — list of [remark plugins][remark-plugins] to use
270* `remarkRehypeOptions` ([`Options` from
271 `remark-rehype`][remark-rehype-options], optional)
272 — options to pass through to `remark-rehype`
273* `skipHtml` (`boolean`, default: `false`)
274 — ignore HTML in markdown completely
275* `unwrapDisallowed` (`boolean`, default: `false`)
276 — extract (unwrap) what’s in disallowed elements;
277 normally when say `strong` is not allowed, it and it’s children are dropped,
278 with `unwrapDisallowed` the element itself is replaced by its children
279* `urlTransform` ([`UrlTransform`][api-url-transform], default:
280 [`defaultUrlTransform`][api-default-url-transform])
281 — change URLs
282
283### `UrlTransform`
284
285Transform URLs (TypeScript type).
286
287###### Parameters
288
289* `url` (`string`)
290 — URL
291* `key` (`string`, example: `'href'`)
292 — property name
293* `node` ([`Element` from `hast`][hast-element])
294 — element to check
295
296###### Returns
297
298Transformed URL (`string`, optional).
299
300## Examples
301
302### Use a plugin
303
304This example shows how to use a remark plugin.
305In this case, [`remark-gfm`][remark-gfm], which adds support for strikethrough,
306tables, tasklists and URLs directly:
307
308```jsx
309import React from 'react'
310import {createRoot} from 'react-dom/client'
311import Markdown from 'react-markdown'
312import remarkGfm from 'remark-gfm'
313
314const markdown = `A paragraph with *emphasis* and **strong importance**.
315
316> A block quote with ~strikethrough~ and a URL: https://reactjs.org.
317
318* Lists
319* [ ] todo
320* [x] done
321
322A table:
323
324| a | b |
325| - | - |
326`
327
328createRoot(document.body).render(
329 <Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
330)
331```
332
333<details>
334<summary>Show equivalent JSX</summary>
335
336```jsx
337<>
338 <p>
339 A paragraph with <em>emphasis</em> and <strong>strong importance</strong>.
340 </p>
341 <blockquote>
342 <p>
343 A block quote with <del>strikethrough</del> and a URL:{' '}
344 <a href="https://reactjs.org">https://reactjs.org</a>.
345 </p>
346 </blockquote>
347 <ul className="contains-task-list">
348 <li>Lists</li>
349 <li className="task-list-item">
350 <input type="checkbox" disabled /> todo
351 </li>
352 <li className="task-list-item">
353 <input type="checkbox" disabled checked /> done
354 </li>
355 </ul>
356 <p>A table:</p>
357 <table>
358 <thead>
359 <tr>
360 <th>a</th>
361 <th>b</th>
362 </tr>
363 </thead>
364 </table>
365</>
366```
367
368</details>
369
370### Use a plugin with options
371
372This example shows how to use a plugin and give it options.
373To do that, use an array with the plugin at the first place, and the options
374second.
375[`remark-gfm`][remark-gfm] has an option to allow only double tildes for
376strikethrough:
377
378```jsx
379import React from 'react'
380import {createRoot} from 'react-dom/client'
381import Markdown from 'react-markdown'
382import remarkGfm from 'remark-gfm'
383
384const markdown = 'This ~is not~ strikethrough, but ~~this is~~!'
385
386createRoot(document.body).render(
387 <Markdown remarkPlugins={[[remarkGfm, {singleTilde: false}]]}>
388 {markdown}
389 </Markdown>
390)
391```
392
393<details>
394<summary>Show equivalent JSX</summary>
395
396```jsx
397<p>
398 This ~is not~ strikethrough, but <del>this is</del>!
399</p>
400```
401
402</details>
403
404### Use custom components (syntax highlight)
405
406This example shows how you can overwrite the normal handling of an element by
407passing a component.
408In this case, we apply syntax highlighting with the seriously super amazing
409[`react-syntax-highlighter`][react-syntax-highlighter] by
410[**@conorhastings**][conor]:
411
412<!-- To do: currently broken on actual ESM; let’s find an alternative? -->
413
414```jsx
415import React from 'react'
416import {createRoot} from 'react-dom/client'
417import Markdown from 'react-markdown'
418import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
419import {dark} from 'react-syntax-highlighter/dist/esm/styles/prism'
420
421// Did you know you can use tildes instead of backticks for code in markdown? ✨
422const markdown = `Here is some JavaScript code:
423
424~~~js
425console.log('It works!')
426~~~
427`
428
429createRoot(document.body).render(
430 <Markdown
431 children={markdown}
432 components={{
433 code(props) {
434 const {children, className, node, ...rest} = props
435 const match = /language-(\w+)/.exec(className || '')
436 return match ? (
437 <SyntaxHighlighter
438 {...rest}
439 PreTag="div"
440 children={String(children).replace(/\n$/, '')}
441 language={match[1]}
442 style={dark}
443 />
444 ) : (
445 <code {...rest} className={className}>
446 {children}
447 </code>
448 )
449 }
450 }}
451 />
452)
453```
454
455<details>
456<summary>Show equivalent JSX</summary>
457
458```jsx
459<>
460 <p>Here is some JavaScript code:</p>
461 <pre>
462 <SyntaxHighlighter language="js" style={dark} PreTag="div" children="console.log('It works!')" />
463 </pre>
464</>
465```
466
467</details>
468
469### Use remark and rehype plugins (math)
470
471This example shows how a syntax extension (through [`remark-math`][remark-math])
472is used to support math in markdown, and a transform plugin
473([`rehype-katex`][rehype-katex]) to render that math.
474
475```jsx
476import React from 'react'
477import {createRoot} from 'react-dom/client'
478import Markdown from 'react-markdown'
479import rehypeKatex from 'rehype-katex'
480import remarkMath from 'remark-math'
481import 'katex/dist/katex.min.css' // `rehype-katex` does not import the CSS for you
482
483const markdown = `The lift coefficient ($C_L$) is a dimensionless coefficient.`
484
485createRoot(document.body).render(
486 <Markdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>
487 {markdown}
488 </Markdown>
489)
490```
491
492<details>
493<summary>Show equivalent JSX</summary>
494
495```jsx
496<p>
497 The lift coefficient (
498 <span className="katex">
499 <span className="katex-mathml">
500 <math xmlns="http://www.w3.org/1998/Math/MathML">{/* … */}</math>
501 </span>
502 <span className="katex-html" aria-hidden="true">
503 {/* … */}
504 </span>
505 </span>
506 ) is a dimensionless coefficient.
507</p>
508```
509
510</details>
511
512## Plugins
513
514We use [unified][], specifically [remark][] for markdown and [rehype][] for
515HTML, which are tools to transform content with plugins.
516Here are three good ways to find plugins:
517
518* [`awesome-remark`][awesome-remark] and [`awesome-rehype`][awesome-rehype]
519 — selection of the most awesome projects
520* [List of remark plugins][remark-plugins] and
521 [list of rehype plugins][rehype-plugins]
522 — list of all plugins
523* [`remark-plugin`][remark-plugin] and [`rehype-plugin`][rehype-plugin] topics
524 — any tagged repo on GitHub
525
526## Syntax
527
528`react-markdown` follows CommonMark, which standardizes the differences between
529markdown implementations, by default.
530Some syntax extensions are supported through plugins.
531
532We use [`micromark`][micromark] under the hood for our parsing.
533See its documentation for more information on markdown, CommonMark, and
534extensions.
535
536## Types
537
538This package is fully typed with [TypeScript][].
539It exports the additional types
540[`AllowElement`][api-allow-element],
541[`ExtraProps`][api-extra-props],
542[`Components`][api-components],
543[`Options`][api-options], and
544[`UrlTransform`][api-url-transform].
545
546## Compatibility
547
548Projects maintained by the unified collective are compatible with maintained
549versions of Node.js.
550
551When we cut a new major release, we drop support for unmaintained versions of
552Node.
553This means we try to keep the current release line, `react-markdown@^9`,
554compatible with Node.js 16.
555
556They work in all modern browsers (essentially: everything not IE 11).
557You can use a bundler (such as esbuild, webpack, or Rollup) to use this package
558in your project, and use its options (or plugins) to add support for legacy
559browsers.
560
561## Architecture
562
563<pre><code> react-markdown
564 +----------------------------------------------------------------------------------------------------------------+
565 | |
566 | +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
567 | | | | | | | | | | | |
568<a href="https://commonmark.org">markdown</a>-+->+ <a href="https://github.com/remarkjs/remark">remark</a> +-<a href="https://github.com/syntax-tree/mdast">mdast</a>->+ <a href="https://github.com/remarkjs/remark/blob/main/doc/plugins.md">remark plugins</a> +-<a href="https://github.com/syntax-tree/mdast">mdast</a>->+ <a href="https://github.com/remarkjs/remark-rehype">remark-rehype</a> +-<a href="https://github.com/syntax-tree/hast">hast</a>->+ <a href="https://github.com/rehypejs/rehype/blob/main/doc/plugins.md">rehype plugins</a> +-<a href="https://github.com/syntax-tree/hast">hast</a>->+ <a href="#appendix-b-components">components</a> +-+->react elements
569 | | | | | | | | | | | |
570 | +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
571 | |
572 +----------------------------------------------------------------------------------------------------------------+
573</code></pre>
574
575To understand what this project does, it’s important to first understand what
576unified does: please read through the [`unifiedjs/unified`][unified] readme (the
577part until you hit the API section is required reading).
578
579`react-markdown` is a unified pipeline — wrapped so that most folks don’t need
580to directly interact with unified.
581The processor goes through these steps:
582
583* parse markdown to mdast (markdown syntax tree)
584* transform through remark (markdown ecosystem)
585* transform mdast to hast (HTML syntax tree)
586* transform through rehype (HTML ecosystem)
587* render hast to React with components
588
589## Appendix A: HTML in markdown
590
591`react-markdown` typically escapes HTML (or ignores it, with `skipHtml`)
592because it is dangerous and defeats the purpose of this library.
593
594However, if you are in a trusted environment (you trust the markdown), and
595can spare the bundle size (±60kb minzipped), then you can use
596[`rehype-raw`][rehype-raw]:
597
598```jsx
599import React from 'react'
600import {createRoot} from 'react-dom/client'
601import Markdown from 'react-markdown'
602import rehypeRaw from 'rehype-raw'
603
604const markdown = `<div class="note">
605
606Some *emphasis* and <strong>strong</strong>!
607
608</div>`
609
610createRoot(document.body).render(
611 <Markdown rehypePlugins={[rehypeRaw]}>{markdown}</Markdown>
612)
613```
614
615<details>
616<summary>Show equivalent JSX</summary>
617
618```jsx
619<div className="note">
620 <p>
621 Some <em>emphasis</em> and <strong>strong</strong>!
622 </p>
623</div>
624```
625
626</details>
627
628**Note**: HTML in markdown is still bound by how [HTML works in
629CommonMark][commonmark-html].
630Make sure to use blank lines around block-level HTML that again contains
631markdown!
632
633## Appendix B: Components
634
635You can also change the things that come from markdown:
636
637```jsx
638<Markdown
639 components={{
640 // Map `h1` (`# heading`) to use `h2`s.
641 h1: 'h2',
642 // Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
643 em(props) {
644 const {node, ...rest} = props
645 return <i style={{color: 'red'}} {...rest} />
646 }
647 }}
648/>
649```
650
651The keys in components are HTML equivalents for the things you write with
652markdown (such as `h1` for `# heading`).
653Normally, in markdown, those are: `a`, `blockquote`, `br`, `code`, `em`, `h1`,
654`h2`, `h3`, `h4`, `h5`, `h6`, `hr`, `img`, `li`, `ol`, `p`, `pre`, `strong`, and
655`ul`.
656With [`remark-gfm`][remark-gfm], you can also use `del`, `input`, `table`,
657`tbody`, `td`, `th`, `thead`, and `tr`.
658Other remark or rehype plugins that add support for new constructs will also
659work with `react-markdown`.
660
661The props that are passed are what you probably would expect: an `a` (link) will
662get `href` (and `title`) props, and `img` (image) an `src`, `alt` and `title`,
663etc.
664
665Every component will receive a `node`.
666This is the original [`Element` from `hast`][hast-element] element being turned
667into a React element.
668
669## Appendix C: line endings in markdown (and JSX)
670
671You might have trouble with how line endings work in markdown and JSX.
672We recommend the following, which solves all line ending problems:
673
674```jsx
675// If you write actual markdown in your code, put your markdown in a variable;
676// **do not indent markdown**:
677const markdown = `
678# This is perfect!
679`
680
681// Pass the value as an expresion as an only child:
682const result = <Markdown>{markdown}</Markdown>
683```
684
685👆 That works.
686Read on for what doesn’t and why that is.
687
688You might try to write markdown directly in your JSX and find that it **does
689not** work:
690
691```jsx
692<Markdown>
693 # Hi
694
695 This is **not** a paragraph.
696</Markdown>
697```
698
699The is because in JSX the whitespace (including line endings) is collapsed to
700a single space.
701So the above example is equivalent to:
702
703```jsx
704<Markdown> # Hi This is **not** a paragraph. </Markdown>
705```
706
707Instead, to pass markdown to `Markdown`, you can use an expression:
708with a template literal:
709
710```jsx
711<Markdown>{`
712# Hi
713
714This is a paragraph.
715`}</Markdown>
716```
717
718Template literals have another potential problem, because they keep whitespace
719(including indentation) inside them.
720That means that the following **does not** turn into a heading:
721
722```jsx
723<Markdown>{`
724 # This is **not** a heading, it’s an indented code block
725`}</Markdown>
726```
727
728## Security
729
730Use of `react-markdown` is secure by default.
731Overwriting `urlTransform` to something insecure will open you up to XSS
732vectors.
733Furthermore, the `remarkPlugins`, `rehypePlugins`, and `components` you use may
734be insecure.
735
736To make sure the content is completely safe, even after what plugins do,
737use [`rehype-sanitize`][rehype-sanitize].
738It lets you define your own schema of what is and isn’t allowed.
739
740## Related
741
742* [`MDX`][mdx]
743 — JSX *in* markdown
744* [`remark-gfm`][remark-gfm]
745 — add support for GitHub flavored markdown support
746* [`react-remark`][react-remark]
747 — hook based alternative
748* [`rehype-react`][rehype-react]
749 — turn HTML into React elements
750
751## Contribute
752
753See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways
754to get started.
755See [`support.md`][support] for ways to get help.
756
757This project has a [code of conduct][coc].
758By interacting with this repository, organization, or community you agree to
759abide by its terms.
760
761## License
762
763[MIT][license] © [Espen Hovlandsdal][author]
764
765[build-badge]: https://github.com/remarkjs/react-markdown/workflows/main/badge.svg
766
767[build]: https://github.com/remarkjs/react-markdown/actions
768
769[coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/react-markdown.svg
770
771[coverage]: https://codecov.io/github/remarkjs/react-markdown
772
773[downloads-badge]: https://img.shields.io/npm/dm/react-markdown.svg
774
775[downloads]: https://www.npmjs.com/package/react-markdown
776
777[size-badge]: https://img.shields.io/bundlejs/size/react-markdown
778
779[size]: https://bundlejs.com/?q=react-markdown
780
781[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
782
783[backers-badge]: https://opencollective.com/unified/backers/badge.svg
784
785[collective]: https://opencollective.com/unified
786
787[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg
788
789[chat]: https://github.com/remarkjs/remark/discussions
790
791[npm]: https://docs.npmjs.com/cli/install
792
793[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
794
795[esmsh]: https://esm.sh
796
797[health]: https://github.com/remarkjs/.github
798
799[coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md
800
801[contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md
802
803[support]: https://github.com/remarkjs/.github/blob/main/support.md
804
805[license]: license
806
807[author]: https://espen.codes/
808
809[awesome-remark]: https://github.com/remarkjs/awesome-remark
810
811[awesome-rehype]: https://github.com/rehypejs/awesome-rehype
812
813[commonmark-help]: https://commonmark.org/help/
814
815[commonmark-html]: https://spec.commonmark.org/0.30/#html-blocks
816
817[hast-element]: https://github.com/syntax-tree/hast#element
818
819[hast-node]: https://github.com/syntax-tree/hast#nodes
820
821[mdx]: https://github.com/mdx-js/mdx/
822
823[micromark]: https://github.com/micromark/micromark
824
825[react]: http://reactjs.org
826
827[react-remark]: https://github.com/remarkjs/react-remark
828
829[react-syntax-highlighter]: https://github.com/react-syntax-highlighter/react-syntax-highlighter
830
831[rehype]: https://github.com/rehypejs/rehype
832
833[rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex
834
835[rehype-plugin]: https://github.com/topics/rehype-plugin
836
837[rehype-plugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins
838
839[rehype-react]: https://github.com/rehypejs/rehype-react
840
841[rehype-raw]: https://github.com/rehypejs/rehype-raw
842
843[rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
844
845[remark]: https://github.com/remarkjs/remark
846
847[remark-gfm]: https://github.com/remarkjs/remark-gfm
848
849[remark-math]: https://github.com/remarkjs/remark-math
850
851[remark-plugin]: https://github.com/topics/remark-plugin
852
853[remark-plugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
854
855[remark-rehype-options]: https://github.com/remarkjs/remark-rehype#options
856
857[unified]: https://github.com/unifiedjs/unified
858
859[typescript]: https://www.typescriptlang.org
860
861[conor]: https://github.com/conorhastings
862
863[demo]: https://remarkjs.github.io/react-markdown/
864
865[section-components]: #appendix-b-components
866
867[section-plugins]: #plugins
868
869[section-security]: #security
870
871[section-syntax]: #syntax
872
873[api-allow-element]: #allowelement
874
875[api-components]: #components
876
877[api-default-url-transform]: #defaulturltransformurl
878
879[api-extra-props]: #extraprops
880
881[api-markdown]: #markdown
882
883[api-options]: #options
884
885[api-url-transform]: #urltransform