1 | # react-markdown
|
2 |
|
3 | [![Build][build-badge]][build]
|
4 | [![Coverage][coverage-badge]][coverage]
|
5 | [![Downloads][downloads-badge]][downloads]
|
6 | [![Size][size-badge]][size]
|
7 | [![Sponsors][sponsors-badge]][collective]
|
8 | [![Backers][backers-badge]][collective]
|
9 | [![Chat][chat-badge]][chat]
|
10 |
|
11 | Markdown component for React using [**remark**][remark].
|
12 |
|
13 | [Learn markdown here][learn] and [check out the demo here][demo].
|
14 |
|
15 | ## Install
|
16 |
|
17 | This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c):
|
18 | Node 12+ is needed to use it and it must be `import`ed instead of `require`d.
|
19 |
|
20 | [npm][]:
|
21 |
|
22 | ```sh
|
23 | npm install react-markdown
|
24 | ```
|
25 |
|
26 | ## Why this one?
|
27 |
|
28 | There are other ways for markdown in React out there so why use this one?
|
29 | The two main reasons are that they often rely on `dangerouslySetInnerHTML` or
|
30 | have bugs with how they handle markdown.
|
31 | `react-markdown` uses a syntax tree to build the virtual dom which allows for
|
32 | updating only the changing DOM instead of completely overwriting.
|
33 | `react-markdown` is 100% CommonMark (optionally GFM) compliant and has
|
34 | extensions to support custom syntax.
|
35 |
|
36 | ## Use
|
37 |
|
38 | A basic hello world:
|
39 |
|
40 | ```jsx
|
41 | import React from 'react'
|
42 | import ReactMarkdown from 'react-markdown'
|
43 | import ReactDom from 'react-dom'
|
44 |
|
45 | ReactDom.render(<ReactMarkdown># Hello, *world*!</ReactMarkdown>, document.body)
|
46 | ```
|
47 |
|
48 | <details>
|
49 | <summary>Show equivalent JSX</summary>
|
50 |
|
51 | ```jsx
|
52 | <h1>
|
53 | Hello, <em>world</em>!
|
54 | </h1>
|
55 | ```
|
56 |
|
57 | </details>
|
58 |
|
59 | Here is an example that shows passing the markdown as a string and how
|
60 | to use a plugin ([`remark-gfm`][gfm], which adds support for strikethrough,
|
61 | tables, tasklists and URLs directly):
|
62 |
|
63 | ```jsx
|
64 | import React from 'react'
|
65 | import ReactDom from 'react-dom'
|
66 | import ReactMarkdown from 'react-markdown'
|
67 | import remarkGfm from 'remark-gfm'
|
68 |
|
69 | const markdown = `Just a link: https://reactjs.com.`
|
70 |
|
71 | ReactDom.render(
|
72 | <ReactMarkdown children={markdown} remarkPlugins={[remarkGfm]} />,
|
73 | document.body
|
74 | )
|
75 | ```
|
76 |
|
77 | <details>
|
78 | <summary>Show equivalent JSX</summary>
|
79 |
|
80 | ```jsx
|
81 | <p>
|
82 | Just a link: <a href="https://reactjs.com">https://reactjs.com</a>.
|
83 | </p>
|
84 | ```
|
85 |
|
86 | </details>
|
87 |
|
88 | ## API
|
89 |
|
90 | This package exports the following identifier: `uriTransformer`.
|
91 | The default export is `ReactMarkdown`.
|
92 |
|
93 | ### `props`
|
94 |
|
95 | * `children` (`string`, default: `''`)\
|
96 | Markdown to parse
|
97 | * `className` (`string?`)\
|
98 | Wrap the markdown in a `div` with this class name
|
99 | * `skipHtml` (`boolean`, default: `false`)\
|
100 | Ignore HTML in Markdown completely
|
101 | * `sourcePos` (`boolean`, default: `false`)\
|
102 | Pass a prop to all components with a serialized position
|
103 | (`data-sourcepos="3:1-3:13"`)
|
104 | * `rawSourcePos` (`boolean`, default: `false`)\
|
105 | Pass a prop to all components with their [position][]
|
106 | (`sourcePosition: {start: {line: 3, column: 1}, end:…}`)
|
107 | * `includeElementIndex` (`boolean`, default: `false`)\
|
108 | Pass the `index` (number of elements before it) and `siblingCount` (number
|
109 | of elements in parent) as props to all components
|
110 | * `allowedElements` (`Array.<string>`, default: `undefined`)\
|
111 | Tag names to allow (can’t combine w/ `disallowedElements`).
|
112 | By default all elements are allowed
|
113 | * `disallowedElements` (`Array.<string>`, default: `undefined`)\
|
114 | Tag names to disallow (can’t combine w/ `allowedElements`).
|
115 | By default no elements are disallowed
|
116 | * `allowElement` (`(element, index, parent) => boolean?`, optional)\
|
117 | Function called to check if an element is allowed (when truthy) or not.
|
118 | `allowedElements` / `disallowedElements` is used first!
|
119 | * `unwrapDisallowed` (`boolean`, default: `false`)\
|
120 | Extract (unwrap) the children of not allowed elements.
|
121 | By default, when `strong` is not allowed, it and it’s children is dropped,
|
122 | but with `unwrapDisallowed` the element itself is dropped but the children
|
123 | used
|
124 | * `linkTarget` (`string` or `(href, children, title) => string`, optional)\
|
125 | Target to use on links (such as `_blank` for `<a target="_blank"…`)
|
126 | * `transformLinkUri` (`(href, children, title) => string`, default:
|
127 | [`./uri-transformer.js`][uri], optional)\
|
128 | URL to use for links.
|
129 | The default allows only `http`, `https`, `mailto`, and `tel`, and is
|
130 | exported from this module as `uriTransformer`.
|
131 | Pass `null` to allow all URLs.
|
132 | See [security][]
|
133 | * `transformImageUri` (`(src, alt, title) => string`, default:
|
134 | [`./uri-transformer.js`][uri], optional)\
|
135 | Same as `transformLinkUri` but for images
|
136 | * `components` (`Object.<string, Component>`, default: `{}`)\
|
137 | Object mapping tag names to React components
|
138 | * `remarkPlugins` (`Array.<Plugin>`, default: `[]`)\
|
139 | List of [remark plugins][remark-plugins] to use.
|
140 | See the next section for examples on how to pass options
|
141 | * `rehypePlugins` (`Array.<Plugin>`, default: `[]`)\
|
142 | List of [rehype plugins][rehype-plugins] to use.
|
143 | See the next section for examples on how to pass options
|
144 |
|
145 | ## Examples
|
146 |
|
147 | ### Use a plugin
|
148 |
|
149 | This example shows how to use a remark plugin.
|
150 | In this case, [`remark-gfm`][gfm], which adds support for
|
151 | strikethrough, tables, tasklists and URLs directly:
|
152 |
|
153 | ```jsx
|
154 | import React from 'react'
|
155 | import ReactMarkdown from 'react-markdown'
|
156 | import ReactDom from 'react-dom'
|
157 | import remarkGfm from 'remark-gfm'
|
158 |
|
159 | const markdown = `A paragraph with *emphasis* and **strong importance**.
|
160 |
|
161 | > A block quote with ~strikethrough~ and a URL: https://reactjs.org.
|
162 |
|
163 | * Lists
|
164 | * [ ] todo
|
165 | * [x] done
|
166 |
|
167 | A table:
|
168 |
|
169 | | a | b |
|
170 | | - | - |
|
171 | `
|
172 |
|
173 | ReactDom.render(
|
174 | <ReactMarkdown children={markdown} remarkPlugins={[remarkGfm]} />,
|
175 | document.body
|
176 | )
|
177 | ```
|
178 |
|
179 | <details>
|
180 | <summary>Show equivalent JSX</summary>
|
181 |
|
182 | ```jsx
|
183 | <>
|
184 | <p>
|
185 | A paragraph with <em>emphasis</em> and <strong>strong importance</strong>.
|
186 | </p>
|
187 | <blockquote>
|
188 | <p>
|
189 | A block quote with <del>strikethrough</del> and a URL:{' '}
|
190 | <a href="https://reactjs.org">https://reactjs.org</a>.
|
191 | </p>
|
192 | </blockquote>
|
193 | <ul>
|
194 | <li>Lists</li>
|
195 | <li>
|
196 | <input checked={false} readOnly={true} type="checkbox" /> todo
|
197 | </li>
|
198 | <li>
|
199 | <input checked={true} readOnly={true} type="checkbox" /> done
|
200 | </li>
|
201 | </ul>
|
202 | <p>A table:</p>
|
203 | <table>
|
204 | <thead>
|
205 | <tr>
|
206 | <td>a</td>
|
207 | <td>b</td>
|
208 | </tr>
|
209 | </thead>
|
210 | </table>
|
211 | </>
|
212 | ```
|
213 |
|
214 | </details>
|
215 |
|
216 | ### Use a plugin with options
|
217 |
|
218 | This example shows how to use a plugin and give it options.
|
219 | To do that, use an array with the plugin at the first place, and the options
|
220 | second.
|
221 | [`remark-gfm`][gfm] has an option to allow only double tildes for strikethrough:
|
222 |
|
223 | ```jsx
|
224 | import React from 'react'
|
225 | import ReactMarkdown from 'react-markdown'
|
226 | import ReactDom from 'react-dom'
|
227 | import remarkGfm from 'remark-gfm'
|
228 |
|
229 | ReactDom.render(
|
230 | <ReactMarkdown remarkPlugins={[[remarkGfm, {singleTilde: false}]]}>
|
231 | This ~is not~ strikethrough, but ~~this is~~!
|
232 | </ReactMarkdown>,
|
233 | document.body
|
234 | )
|
235 | ```
|
236 |
|
237 | <details>
|
238 | <summary>Show equivalent JSX</summary>
|
239 |
|
240 | ```jsx
|
241 | <p>
|
242 | This ~is not~ strikethrough, but <del>this is</del>!
|
243 | </p>
|
244 | ```
|
245 |
|
246 | </details>
|
247 |
|
248 | ### Use custom components (syntax highlight)
|
249 |
|
250 | This example shows how you can overwrite the normal handling of an element by
|
251 | passing a component.
|
252 | In this case, we apply syntax highlighting with the seriously super amazing
|
253 | [`react-syntax-highlighter`][react-syntax-highlighter] by
|
254 | [**@conorhastings**][conor]:
|
255 |
|
256 | ```jsx
|
257 | import React from 'react'
|
258 | import ReactDom from 'react-dom'
|
259 | import ReactMarkdown from 'react-markdown'
|
260 | import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
|
261 | import {dark} from 'react-syntax-highlighter/dist/esm/styles/prism'
|
262 |
|
263 | // Did you know you can use tildes instead of backticks for code in markdown? ✨
|
264 | const markdown = `Here is some JavaScript code:
|
265 |
|
266 | ~~~js
|
267 | console.log('It works!')
|
268 | ~~~
|
269 | `
|
270 |
|
271 | ReactDom.render(
|
272 | <ReactMarkdown
|
273 | children={markdown}
|
274 | components={{
|
275 | code({node, inline, className, children, ...props}) {
|
276 | const match = /language-(\w+)/.exec(className || '')
|
277 | return !inline && match ? (
|
278 | <SyntaxHighlighter
|
279 | children={String(children).replace(/\n$/, '')}
|
280 | style={dark}
|
281 | language={match[1]}
|
282 | PreTag="div"
|
283 | {...props}
|
284 | />
|
285 | ) : (
|
286 | <code className={className} {...props}>
|
287 | {children}
|
288 | </code>
|
289 | )
|
290 | }
|
291 | }}
|
292 | />,
|
293 | document.body
|
294 | )
|
295 | ```
|
296 |
|
297 | <details>
|
298 | <summary>Show equivalent JSX</summary>
|
299 |
|
300 | ```jsx
|
301 | <>
|
302 | <p>Here is some JavaScript code:</p>
|
303 | <pre>
|
304 | <SyntaxHighlighter language="js" style={dark} PreTag="div" children="console.log('It works!')" />
|
305 | </pre>
|
306 | </>
|
307 | ```
|
308 |
|
309 | </details>
|
310 |
|
311 | ### Use remark and rehype plugins (math)
|
312 |
|
313 | This example shows how a syntax extension (through [`remark-math`][math])
|
314 | is used to support math in markdown, and a transform plugin
|
315 | ([`rehype-katex`][katex]) to render that math.
|
316 |
|
317 | ```jsx
|
318 | import React from 'react'
|
319 | import ReactDom from 'react-dom'
|
320 | import ReactMarkdown from 'react-markdown'
|
321 | import remarkMath from 'remark-math'
|
322 | import rehypeKatex from 'rehype-katex'
|
323 |
|
324 | import 'katex/dist/katex.min.css' // `rehype-katex` does not import the CSS for you
|
325 |
|
326 | ReactDom.render(
|
327 | <ReactMarkdown
|
328 | children={`The lift coefficient ($C_L$) is a dimensionless coefficient.`}
|
329 | remarkPlugins={[remarkMath]}
|
330 | rehypePlugins={[rehypeKatex]}
|
331 | />,
|
332 | document.body
|
333 | )
|
334 | ```
|
335 |
|
336 | <details>
|
337 | <summary>Show equivalent JSX</summary>
|
338 |
|
339 | ```jsx
|
340 | <p>
|
341 | The lift coefficient (
|
342 | <span className="math math-inline">
|
343 | <span className="katex">
|
344 | <span className="katex-mathml">
|
345 | <math xmlns="http://www.w3.org/1998/Math/MathML">{/* … */}</math>
|
346 | </span>
|
347 | <span className="katex-html" aria-hidden="true">
|
348 | {/* … */}
|
349 | </span>
|
350 | </span>
|
351 | </span>
|
352 | ) is a dimensionless coefficient.
|
353 | </p>
|
354 | ```
|
355 |
|
356 | </details>
|
357 |
|
358 | ## Architecture
|
359 |
|
360 | ```txt
|
361 | react-markdown
|
362 | +-------------------------------------------------------------------------------------------------------------------------------------------+
|
363 | | |
|
364 | | +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
|
365 | | | | | | | | | | | | |
|
366 | | -markdown->+ remark +-mdast->+ remark plugins +-mdast->+ remark-rehype +-hast->+ rehype plugins +-hast->+ components +-react elements-> |
|
367 | | | | | | | | | | | | |
|
368 | | +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
|
369 | | |
|
370 | +-------------------------------------------------------------------------------------------------------------------------------------------+
|
371 | ```
|
372 |
|
373 | relevant links: [markdown](https://commonmark.org), [remark](https://github.com/remarkjs/remark), [mdast](https://github.com/syntax-tree/mdast), [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md), [remark-rehype](https://github.com/remarkjs/remark-rehype), [hast](https://github.com/syntax-tree/hast), [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md), [components](#appendix-b-components)
|
374 |
|
375 | To understand what this project does, it’s very important to first understand
|
376 | what unified does: please read through the [`unifiedjs/unified`](https://github.com/unifiedjs/unified)
|
377 | readme (the part until you hit the API section is required reading).
|
378 |
|
379 | react-markdown is a unified pipeline — wrapped so that most folks don’t need to
|
380 | directly interact with unified. The processor goes through these steps:
|
381 |
|
382 | * Parse Markdown to mdast (markdown syntax tree)
|
383 | * Transform through remark (markdown ecosystem)
|
384 | * Transform mdast to hast (HTML syntax tree)
|
385 | * Transform through rehype (HTML ecosystem)
|
386 | * Render hast to react with components
|
387 |
|
388 | ## Appendix A: HTML in markdown
|
389 |
|
390 | `react-markdown` typically escapes HTML (or ignores it, with `skipHtml`)
|
391 | because it is dangerous and defeats the purpose of this library.
|
392 |
|
393 | However, if you are in a trusted environment (you trust the markdown), and
|
394 | can spare the bundle size (±60kb minzipped), then you can use
|
395 | [`rehype-raw`][raw]:
|
396 |
|
397 | ```jsx
|
398 | import React from 'react'
|
399 | import ReactDom from 'react-dom'
|
400 | import ReactMarkdown from 'react-markdown'
|
401 | import rehypeRaw from 'rehype-raw'
|
402 |
|
403 | const input = `<div class="note">
|
404 |
|
405 | Some *emphasis* and <strong>strong</strong>!
|
406 |
|
407 | </div>`
|
408 |
|
409 | ReactDom.render(
|
410 | <ReactMarkdown rehypePlugins={[rehypeRaw]} children={input} />,
|
411 | document.body
|
412 | )
|
413 | ```
|
414 |
|
415 | <details>
|
416 | <summary>Show equivalent JSX</summary>
|
417 |
|
418 | ```jsx
|
419 | <div class="note">
|
420 | <p>Some <em>emphasis</em> and <strong>strong</strong>!</p>
|
421 | </div>
|
422 | ```
|
423 |
|
424 | </details>
|
425 |
|
426 | **Note**: HTML in markdown is still bound by how [HTML works in
|
427 | CommonMark][cm-html].
|
428 | Make sure to use blank lines around block-level HTML that again contains
|
429 | markdown!
|
430 |
|
431 | ## Appendix B: Components
|
432 |
|
433 | You can also change the things that come from markdown:
|
434 |
|
435 | ```js
|
436 | <ReactMarkdown
|
437 | components={{
|
438 | // Map `h1` (`# heading`) to use `h2`s.
|
439 | h1: 'h2',
|
440 | // Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
|
441 | em: ({node, ...props}) => <i style={{color: 'red'}} {...props} />
|
442 | }}
|
443 | />
|
444 | ```
|
445 |
|
446 | The keys in components are HTML equivalents for the things you write with
|
447 | markdown (such as `h1` for `# heading`)**†**
|
448 |
|
449 | **†** Normally, in markdown, those are: `a`, `blockquote`, `code`, `em`, `h1`,
|
450 | `h2`, `h3`, `h4`, `h5`, `h6`, `hr`, `img`, `li`, `ol`, `p`, `pre`, `strong`, and
|
451 | `ul`.
|
452 | With [`remark-gfm`][gfm], you can also use: `del`, `input`, `table`, `tbody`,
|
453 | `td`, `th`, `thead`, and `tr`.
|
454 | Other remark or rehype plugins that add support for new constructs will also
|
455 | work with `react-markdown`.
|
456 |
|
457 | The props that are passed are what you probably would expect: an `a` (link) will
|
458 | get `href` (and `title`) props, and `img` (image) an `src` (and `title`), etc.
|
459 | There are some extra props passed.
|
460 |
|
461 | * `code`
|
462 | * `inline` (`boolean?`)
|
463 | — set to `true` for inline code
|
464 | * `className` (`string?`)
|
465 | — set to `language-js` or so when using ` ```js `
|
466 | * `h1`, `h2`, `h3`, `h4`, `h5`, `h6`
|
467 | * `level` (`number` beween 1 and 6)
|
468 | — heading rank
|
469 | * `input` (when using [`remark-gfm`][gfm])
|
470 | * `checked` (`boolean`)
|
471 | — whether the item is checked
|
472 | * `disabled` (`true`)
|
473 | * `type` (`'checkbox'`)
|
474 | * `li`
|
475 | * `index` (`number`)
|
476 | — number of preceding items (so first gets `0`, etc.)
|
477 | * `ordered` (`boolean`)
|
478 | — whether the parent is an `ol` or not
|
479 | * `checked` (`boolean?`)
|
480 | — `null` normally, `boolean` when using [`remark-gfm`][gfm]’s tasklists
|
481 | * `className` (`string?`)
|
482 | — set to `task-list-item` when using [`remark-gfm`][gfm] and the
|
483 | item1 is a tasklist
|
484 | * `ol`, `ul`
|
485 | * `depth` (`number`)
|
486 | — number of ancestral lists (so first gets `0`, etc.)
|
487 | * `ordered` (`boolean`)
|
488 | — whether it’s an `ol` or not
|
489 | * `className` (`string?`)
|
490 | — set to `contains-task-list` when using [`remark-gfm`][gfm] and the
|
491 | list contains one or more tasklists
|
492 | * `td`, `th` (when using [`remark-gfm`][gfm])
|
493 | * `style` (`Object?`)
|
494 | — something like `{textAlign: 'left'}` depending on how the cell is
|
495 | aligned
|
496 | * `isHeader` (`boolean`)
|
497 | — whether it’s a `th` or not
|
498 | * `tr` (when using [`remark-gfm`][gfm])
|
499 | * `isHeader` (`boolean`)
|
500 | — whether it’s in the `thead` or not
|
501 |
|
502 | Every component will receive a `node` (`Object`).
|
503 | This is the original [hast](https://github.com/syntax-tree/hast) element being
|
504 | turned into a React element.
|
505 |
|
506 | Every element will receive a `key` (`string`).
|
507 | See [React’s docs](https://reactjs.org/docs/lists-and-keys.html#keys) for more
|
508 | info.
|
509 |
|
510 | Optionally, components will also receive:
|
511 |
|
512 | * `data-sourcepos` (`string`)
|
513 | — see `sourcePos` option
|
514 | * `sourcePosition` (`Object`)
|
515 | — see `rawSourcePos` option
|
516 | * `index` and `siblingCount` (`number`)
|
517 | — see `includeElementIndex` option
|
518 | * `target` on `a` (`string`)
|
519 | — see `linkTarget` option
|
520 |
|
521 | ## Security
|
522 |
|
523 | Use of `react-markdown` is secure by default.
|
524 | Overwriting `transformLinkUri` or `transformImageUri` to something insecure will
|
525 | open you up to XSS vectors.
|
526 | Furthermore, the `remarkPlugins` and `rehypePlugins` you use and `components`
|
527 | you write may be insecure.
|
528 |
|
529 | To make sure the content is completely safe, even after what plugins do,
|
530 | use [`rehype-sanitize`][sanitize].
|
531 | That plugin lets you define your own schema of what is and isn’t allowed.
|
532 |
|
533 | ## Related
|
534 |
|
535 | * [`MDX`](https://github.com/mdx-js/mdx)
|
536 | — JSX *in* markdown
|
537 | * [`remark-gfm`](https://github.com/remarkjs/remark-gfm)
|
538 | — Plugin for GitHub flavored markdown support
|
539 |
|
540 | ## Contribute
|
541 |
|
542 | See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways
|
543 | to get started.
|
544 | See [`support.md`][support] for ways to get help.
|
545 |
|
546 | This project has a [code of conduct][coc].
|
547 | By interacting with this repository, organization, or community you agree to
|
548 | abide by its terms.
|
549 |
|
550 | ## License
|
551 |
|
552 | [MIT][license] © [Espen Hovlandsdal][author]
|
553 |
|
554 | [build-badge]: https://github.com/remarkjs/react-markdown/workflows/main/badge.svg
|
555 |
|
556 | [build]: https://github.com/remarkjs/react-markdown/actions
|
557 |
|
558 | [coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/react-markdown.svg
|
559 |
|
560 | [coverage]: https://codecov.io/github/remarkjs/react-markdown
|
561 |
|
562 | [downloads-badge]: https://img.shields.io/npm/dm/react-markdown.svg
|
563 |
|
564 | [downloads]: https://www.npmjs.com/package/react-markdown
|
565 |
|
566 | [size-badge]: https://img.shields.io/bundlephobia/minzip/react-markdown.svg
|
567 |
|
568 | [size]: https://bundlephobia.com/result?p=react-markdown
|
569 |
|
570 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
|
571 |
|
572 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg
|
573 |
|
574 | [collective]: https://opencollective.com/unified
|
575 |
|
576 | [chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg
|
577 |
|
578 | [chat]: https://github.com/remarkjs/remark/discussions
|
579 |
|
580 | [npm]: https://docs.npmjs.com/cli/install
|
581 |
|
582 | [health]: https://github.com/remarkjs/.github
|
583 |
|
584 | [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md
|
585 |
|
586 | [support]: https://github.com/remarkjs/.github/blob/HEAD/support.md
|
587 |
|
588 | [coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md
|
589 |
|
590 | [license]: license
|
591 |
|
592 | [author]: https://espen.codes/
|
593 |
|
594 | [remark]: https://github.com/remarkjs/remark
|
595 |
|
596 | [demo]: https://remarkjs.github.io/react-markdown/
|
597 |
|
598 | [learn]: https://commonmark.org/help/
|
599 |
|
600 | [position]: https://github.com/syntax-tree/unist#position
|
601 |
|
602 | [gfm]: https://github.com/remarkjs/remark-gfm
|
603 |
|
604 | [math]: https://github.com/remarkjs/remark-math
|
605 |
|
606 | [katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex
|
607 |
|
608 | [raw]: https://github.com/rehypejs/rehype-raw
|
609 |
|
610 | [sanitize]: https://github.com/rehypejs/rehype-sanitize
|
611 |
|
612 | [remark-plugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
|
613 |
|
614 | [rehype-plugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins
|
615 |
|
616 | [cm-html]: https://spec.commonmark.org/0.29/#html-blocks
|
617 |
|
618 | [uri]: https://github.com/remarkjs/react-markdown/blob/main/lib/uri-transformer.js
|
619 |
|
620 | [security]: #security
|
621 |
|
622 | [react-syntax-highlighter]: https://github.com/react-syntax-highlighter/react-syntax-highlighter
|
623 |
|
624 | [conor]: https://github.com/conorhastings
|