Repository: remarkjs/react-markdown
Branch: main
Commit: fda7fa560bec
Files: 15
Total size: 117.7 KB
Directory structure:
gitextract_lk03jjin/
├── .editorconfig
├── .github/
│ └── workflows/
│ ├── bb.yml
│ └── main.yml
├── .gitignore
├── .npmrc
├── .prettierignore
├── changelog.md
├── index.js
├── lib/
│ └── index.js
├── license
├── package.json
├── readme.md
├── script/
│ └── load-jsx.js
├── test.jsx
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
================================================
FILE: .github/workflows/bb.yml
================================================
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: unifiedjs/beep-boop-beta@main
with:
repo-token: ${{secrets.GITHUB_TOKEN}}
name: bb
on:
issues:
types: [closed, edited, labeled, opened, reopened, unlabeled]
pull_request_target:
types: [closed, edited, labeled, opened, reopened, unlabeled]
================================================
FILE: .github/workflows/main.yml
================================================
jobs:
main:
name: ${{matrix.node}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{matrix.node}}
- run: npm install
- run: npm test
- uses: codecov/codecov-action@v5
strategy:
matrix:
node:
- lts/hydrogen
- node
name: main
on:
- pull_request
- push
================================================
FILE: .gitignore
================================================
*.d.ts
*.log
*.map
*.tsbuildinfo
.DS_Store
coverage/
node_modules/
react-markdown.min.js
yarn.lock
================================================
FILE: .npmrc
================================================
ignore-scripts=true
package-lock=false
================================================
FILE: .prettierignore
================================================
coverage/
*.md
================================================
FILE: changelog.md
================================================
# Changelog
All notable changes will be documented in this file.
## 10.0.0 - 2025-02-20
* [`aaaa40b`](https://github.com/remarkjs/react-markdown/commit/aaaa40b)
Remove support for `className` prop
**migrate**: see “Remove `className`” below
### Remove `className`
The `className` prop was removed.
If you want to add classes to some element that wraps the markdown
you can explicitly write that element and add the class to it.
You can then choose yourself which tag name to use and whether to add other
props.
Before:
```js
{markdown}
```
After:
```js
{markdown}
```
## 9.1.0 - 2025-02-20
* [`6ce120e`](https://github.com/remarkjs/react-markdown/commit/6ce120e)
Add support for async plugins
## 9.0.3 - 2025-01-06
(same as 9.0.2 but now with d.ts files)
## 9.0.2 - 2025-01-06
* [`b151a90`](https://github.com/remarkjs/react-markdown/commit/b151a90)
Fix types for React 19
* [`6962af7`](https://github.com/remarkjs/react-markdown/commit/6962af7)
Add declaration maps
* [`aa5933b`](https://github.com/remarkjs/react-markdown/commit/aa5933b)
Refactor to use `@import` to import types
## 9.0.1 - 2023-11-13
* [`d8e3787`](https://github.com/remarkjs/react-markdown/commit/d8e3787)
Fix double encoding in new url transform
## 9.0.0 - 2023-09-27
* [`b67d714`](https://github.com/remarkjs/react-markdown/commit/b67d714)
Change to require Node.js 16\
**migrate**: update too
* [`ec2b134`](https://github.com/remarkjs/react-markdown/commit/ec2b134)
Change to require React 18\
**migrate**: update too
* [`bf5824f`](https://github.com/remarkjs/react-markdown/commit/bf5824f)
Change to use `exports`\
**migrate**: don’t use private APIs
* [`c383a45`](https://github.com/remarkjs/react-markdown/commit/c383a45)
Update `@types/hast`, utilities, plugins, etc\
**migrate**: update too
* [`eca5e6b`](https://github.com/remarkjs/react-markdown/commit/eca5e6b)
[`08ead9e`](https://github.com/remarkjs/react-markdown/commit/08ead9e)
Replace `transformImageUri`, `transformLinkUri` w/ `urlTransform`\
**migrate**: see “Add `urlTransform`” below
* [`de29396`](https://github.com/remarkjs/react-markdown/commit/de29396)
Remove `linkTarget` option\
**migrate**: see “Remove `linkTarget`” below
* [`4346276`](https://github.com/remarkjs/react-markdown/commit/4346276)
Remove support for passing custom props to components\
**migrate**: see “Remove `includeElementIndex`”, “Remove `rawSourcePos`”,
“Remove `sourcePos`”, “Remove extra props passed to certain components”
below
* [`c0dfbd6`](https://github.com/remarkjs/react-markdown/commit/c0dfbd6)
Remove UMD bundle from package\
**migrate**: use `esm.sh` or a CDN or so
* [`e12b5e9`](https://github.com/remarkjs/react-markdown/commit/e12b5e9)
Remove `prop-types`\
**migrate**: use TypeScript
* [`4eb7aa0`](https://github.com/remarkjs/react-markdown/commit/4eb7aa0)
Change to throw errors for removed props\
**migrate**: don’t pass options that don’t do things
* [`8aabf74`](https://github.com/remarkjs/react-markdown/commit/8aabf74)
Change to improve error messages\
**migrate**: expect better messages
### Add `urlTransform`
The `transformImageUri` and `transformLinkUri` were removed.
Having two functions is a bit much, particularly because there are more URLs
you might want to change (or which might be unsafe so *we* make them safe).
And their name and APIs were a bit weird.
You can use the new `urlTransform` prop instead to change all your URLs.
### Remove `linkTarget`
The `linkTarget` option was removed; you should likely not set targets.
If you want to, use
[`rehype-external-links`](https://github.com/rehypejs/rehype-external-links).
### Remove `includeElementIndex`
The `includeElementIndex` option was removed, so `index` is never passed to
components.
Write a plugin to pass `index`:
Show example of plugin
```js
import {visit} from 'unist-util-visit'
function rehypePluginAddingIndex() {
/**
* @param {import('hast').Root} tree
* @returns {undefined}
*/
return function (tree) {
visit(tree, function (node, index) {
if (node.type === 'element' && typeof index === 'number') {
node.properties.index = index
}
})
}
}
```
### Remove `rawSourcePos`
The `rawSourcePos` option was removed, so `sourcePos` is never passed to
components.
All components are passed `node`, so you can get `node.position` from them.
### Remove `sourcePos`
The `sourcePos` option was removed, so `data-sourcepos` is never passed to
elements.
Write a plugin to pass `index`:
Show example of plugin
```js
import {stringifyPosition} from 'unist-util-stringify-position'
import {visit} from 'unist-util-visit'
function rehypePluginAddingIndex() {
/**
* @param {import('hast').Root} tree
* @returns {undefined}
*/
return function (tree) {
visit(tree, function (node) {
if (node.type === 'element') {
node.properties.dataSourcepos = stringifyPosition(node.position)
}
})
}
}
```
### Remove extra props passed to certain components
When overwriting components, these props are no longer passed:
* `inline` on `code`
— create a plugin or use `pre` for the block
* `level` on `h1`, `h2`, `h3`, `h4`, `h5`, `h6`
— check `node.tagName` instead
* `checked` on `li`
— check `task-list-item` class or check `props.children`
* `index` on `li`
— create a plugin
* `ordered` on `li`
— create a plugin or check the parent
* `depth` on `ol`, `ul`
— create a plugin
* `ordered` on `ol`, `ul`
— check `node.tagName` instead
* `isHeader` on `td`, `th`
— check `node.tagName` instead
* `isHeader` on `tr`
— create a plugin or check children
## 8.0.7 - 2023-04-12
* [`c289176`](https://github.com/remarkjs/react-markdown/commit/c289176)
Fix performance for keys
by [**@wooorm**](https://github.com/wooorm)
in [#738](https://github.com/remarkjs/react-markdown/pull/738)
* [`9034dbd`](https://github.com/remarkjs/react-markdown/commit/9034dbd)
Fix types in syntax highlight example
by [**@dlqqq**](https://github.com/dlqqq)
in [#736](https://github.com/remarkjs/react-markdown/pull/736)
**Full Changelog**:
## 8.0.6 - 2023-03-20
* [`33ab015`](https://github.com/remarkjs/react-markdown/commit/33ab015)
Update to TS 5\
by [**@Methuselah96**](https://github.com/Methuselah96)
in [#734](https://github.com/remarkjs/react-markdown/issues/734)
## 8.0.5 - 2023-01-17
* [`d640d40`](https://github.com/remarkjs/react-markdown/commit/d640d40)
Update to use `node16` module resolution in `tsconfig.json`\
by [**@ChristianMurphy**](https://github.com/ChristianMurphy)
in [#723](https://github.com/remarkjs/react-markdown/pull/723)
* [`402fea3`](https://github.com/remarkjs/react-markdown/commit/402fea3)
Fix typo in `plugins` deprecation message\
by [**@marc2332**](https://github.com/marc2332)
in [#719](https://github.com/remarkjs/react-markdown/pull/719)
* [`4f98f73`](https://github.com/remarkjs/react-markdown/commit/4f98f73)
Remove deprecated and unneeded `defaultProps`\
by [**@Lepozepo**](https://github.com/Lepozepo)
in [#718](https://github.com/remarkjs/react-markdown/pull/718)
## 8.0.4 - 2022-12-01
* [`9b20440`](https://github.com/remarkjs/react-markdown/commit/9b20440)
Fix type of `td`, `th` props\
by [**@lucasassisrosa**](https://github.com/lucasassisrosa)
in [#714](https://github.com/remarkjs/react-markdown/pull/714)
* [`cfe075b`](https://github.com/remarkjs/react-markdown/commit/cfe075b)
Add clarification of `alt` on `img` in docs\
by [**@cballenar**](https://github.com/cballenar)
in [#692](https://github.com/remarkjs/react-markdown/pull/692)
## 8.0.3 - 2022-04-20
* [`a2fb833`](https://github.com/remarkjs/react-markdown/commit/a2fb833)
Fix prop types of plugins\
by [**@starpit**](https://github.com/starpit)
in [#683](https://github.com/remarkjs/react-markdown/pull/683)
## 8.0.2 - 2022-03-31
* [`2712227`](https://github.com/remarkjs/react-markdown/commit/2712227)
Update `react-is`
* [`704c3c6`](https://github.com/remarkjs/react-markdown/commit/704c3c6)
Fix TypeScript bug by adding workaround\
by [**@Methuselah96**](https://github.com/Methuselah96)
in [#676](https://github.com/remarkjs/react-markdown/pull/676)
## 8.0.1 - 2022-03-14
* [`c23ecf6`](https://github.com/remarkjs/react-markdown/commit/c23ecf6)
Add missing dependency for types\
by [**@Methuselah96**](https://github.com/Methuselah96)
in [#675](https://github.com/remarkjs/react-markdown/pull/675)
## 8.0.0 - 2022-01-17
* [`cd845c9`](https://github.com/remarkjs/react-markdown/commit/cd845c9)
Remove deprecated `plugins` option\
(**migrate by renaming it to `remarkPlugins`**)
* [`36e4916`](https://github.com/remarkjs/react-markdown/commit/36e4916)
Update [`remark-rehype`](https://github.com/remarkjs/remark-rehype),
add support for passing it options\
by [**@peolic**](https://github.com/peolic)
in [#669](https://github.com/remarkjs/react-markdown/pull/669)\
(**migrate by removing `remark-footnotes` and updating `remark-gfm` if you
were using them, otherwise you shouldn’t notice this**)
## 7.1.2 - 2022-01-02
* [`656a4fa`](https://github.com/remarkjs/react-markdown/commit/656a4fa)
Fix `ref` in types\
by [**@ChristianMurphy**](https://github.com/ChristianMurphy)
in [#668](https://github.com/remarkjs/react-markdown/pull/668)
## 7.1.1 - 2021-11-29
* [`4185f06`](https://github.com/remarkjs/react-markdown/commit/4185f06)
Add improved docs\
by [**@wooorm**](https://github.com/wooorm)
in [#657](https://github.com/remarkjs/react-markdown/pull/657)
## 7.1.0 - 2021-10-21
* [`7b8a829`](https://github.com/remarkjs/react-markdown/commit/7b8a829)
Add support for `SpecialComponents` to be any `ComponentType`\
by [**@Methuselah96**](https://github.com/Methuselah96)
in [#640](https://github.com/remarkjs/react-markdown/pull/640)
* [`a7c26fc`](https://github.com/remarkjs/react-markdown/commit/a7c26fc)
Remove warning on whitespace in tables
## 7.0.1 - 2021-08-26
* [`ec387c2`](https://github.com/remarkjs/react-markdown/commit/ec387c2)
Add improved type for `linkTarget` as string
* [`5af6bc7`](https://github.com/remarkjs/react-markdown/commit/5af6bc7)
Fix to correctly compile intrinsic types
## 7.0.0 - 2021-08-13
Welcome to version 7.
This a major release and therefore contains breaking changes.
### Breaking changes
* [`01b11fe`](https://github.com/remarkjs/react-markdown/commit/01b11fe)
[`c613efd`](https://github.com/remarkjs/react-markdown/commit/c613efd)
[`a1e1c3f`](https://github.com/remarkjs/react-markdown/commit/a1e1c3f)
[`aeee9ac`](https://github.com/remarkjs/react-markdown/commit/aeee9ac)
Use ESM
(please [read this](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c))
* [`3dffd6a`](https://github.com/remarkjs/react-markdown/commit/3dffd6a)
Update dependencies
(upgrade all your plugins and this should go fine)
### Internals
* [`8b5481c`](https://github.com/remarkjs/react-markdown/commit/8b5481c)
[`fb1b512`](https://github.com/remarkjs/react-markdown/commit/fb1b512)
[`144af79`](https://github.com/remarkjs/react-markdown/commit/144af79)
Replace `jest` with `uvu`
* [`8c572df`](https://github.com/remarkjs/react-markdown/commit/8c572df)
Replace `rollup` with `esbuild`
* [`8737eac`](https://github.com/remarkjs/react-markdown/commit/8737eac)
[`28d4c75`](https://github.com/remarkjs/react-markdown/commit/28d4c75)
[`b2dd046`](https://github.com/remarkjs/react-markdown/commit/b2dd046)
Refactor code-style
## 6.0.3 - 2021-07-30
* [`13367ed`](https://github.com/remarkjs/react-markdown/commit/13367ed)
Fix types to include each element w/ its properties
* [`0a1931a`](https://github.com/remarkjs/react-markdown/commit/0a1931a)
Fix to add min version of `property-information`
## 6.0.2 - 2021-05-06
* [`cefc02d`](https://github.com/remarkjs/react-markdown/commit/cefc02d)
Add string type for `className`s
* [`6355e45`](https://github.com/remarkjs/react-markdown/commit/6355e45)
Fix to pass `vfile` to plugins
* [`5cf6e1b`](https://github.com/remarkjs/react-markdown/commit/5cf6e1b)
Fix to add warning when non-strings are given as `children`
## 6.0.1 - 2021-04-23
* [`2e956be`](https://github.com/remarkjs/react-markdown/commit/2e956be)
Fix whitespace in table elements
* [`d36048a`](https://github.com/remarkjs/react-markdown/commit/d36048a)
Add architecture section to readme
## 6.0.0 - 2021-04-15
Welcome to version 6.
This a major release and therefore contains breaking changes.
### Change `renderers` to `components`
`react-markdown` used to let you define components for *markdown* constructs
(`link`, `delete`, `break`, etc).
This proved complex as users didn’t know about those names or markdown
peculiarities (such as that there are fully formed links *and* link references).
See [**GH-549**](https://github.com/remarkjs/react-markdown/issues/549) for more
on why this changed.
See [**Appendix B: Components** in
`readme.md`](https://github.com/remarkjs/react-markdown#appendix-b-components)
for more on components.
Show example of needed change
Before (**broken**):
```js
}}
>{`***`}
```
Now (**fixed**):
```js
}}
>{`***`}
```
Show conversion table
| Type (`renderers`) | Tag names (`components`) |
| ----------------------------------- | --------------------------------------- |
| `blockquote` | `blockquote` |
| `break` | `br` |
| `code`, `inlineCode` | `code`, `pre`**\*** |
| `definition` | **†** |
| `delete` | `del`**‡** |
| `emphasis` | `em` |
| `heading` | `h1`, `h2`, `h3`, `h4`, `h5`, `h6`**§** |
| `html`, `parsedHtml`, `virtualHtml` | **‖** |
| `image`, `imageReference` | `img`**†** |
| `link`, `linkReference` | `a`**†** |
| `list` | `ol`, `ul`**¶** |
| `listItem` | `li` |
| `paragraph` | `p` |
| `root` | **\*\*** |
| `strong` | `strong` |
| `table` | `table`**‡** |
| `tableHead` | `thead`**‡** |
| `tableBody` | `tbody`**‡** |
| `tableRow` | `tr`**‡** |
| `tableCell` | `td`, `th`**‡** |
| `text` | |
| `thematicBreak` | `hr` |
* **\*** It’s possible to differentiate between code based on the `inline`
prop.
Block code is also wrapped in a `pre`
* **†** Resource (`[text](url)`) and reference (`[text][id]`) style links and
images (and their definitions) are now resolved and treated the same
* **‡** Available when using
[`remark-gfm`](https://github.com/remarkjs/remark-gfm)
* **§** It’s possible to differentiate between heading based on the `level`
prop
* **‖** When using `rehype-raw` (see below), components for those elements
can also be used (for example, `abbr` for
`HTML`)
* **¶** It’s possible to differentiate between lists based on the `ordered`
prop
* **\*\*** Wrap `ReactMarkdown` in a component instead
### Add `rehypePlugins`
We’ve added another plugin system:
[**rehype**](https://github.com/rehypejs/rehype).
It’s similar to remark (what we’re using for markdown) but for HTML.
There are many rehype plugins.
Some examples are
[`@mapbox/rehype-prism`](https://github.com/mapbox/rehype-prism)
(syntax highlighting with Prism),
[`rehype-katex`](https://github.com/remarkjs/remark-math/tree/HEAD/packages/rehype-katex)
(rendering math with KaTeX), or
[`rehype-autolink-headings`](https://github.com/rehypejs/rehype-autolink-headings)
(adding links to headings).
See [List of plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md)
for more plugins.
Show example of feature
```js
import rehypeHighlight from 'rehype-highlight'
{`~~~js
console.log(1)
~~~`}
```
### Remove buggy HTML in markdown parser
In a lot of cases, you should not use HTML in markdown: it’s most always unsafe.
And it defeats much of the purpose of this project (not relying on
`dangerouslySetInnerHTML`).
`react-markdown` used to have an opt-in HTML parser with a bunch of bugs.
As we now support rehype plugins, we can defer that work to a rehype plugin.
To support HTML in markdown with `react-markdown`, use
[`rehype-raw`](https://github.com/rehypejs/rehype-raw).
The `astPlugins` and `allowDangerousHtml` (previously called `escapeHtml`) props
are no longer needed and were removed.
When using `rehype-raw`, you should probably use
[`rehype-sanitize`](https://github.com/rehypejs/rehype-sanitize)
too.
Show example of needed change
Before (**broken**):
```js
import MarkdownWithHtml from 'react-markdown/with-html'
{`# Hello, world!`}
```
Now (**fixed**):
```js
import Markdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize from 'rehype-sanitize'
{`# Hello, world!`}
```
### Change `source` to `children`
Instead of passing a `source` pass `children` instead:
Show example of needed change
Before (**broken**):
```js
```
Now (**fixed**):
```js
{`some
markdown`}
```
Or (**also fixed**):
```js
```
### Replace `allowNode`, `allowedTypes`, and `disallowedTypes`
Similar to the `renderers` to `components` change, the filtering options
also changed from being based on markdown names towards being based on HTML
names: `allowNode` to `allowElement`, `allowedTypes` to `allowedElements`, and
`disallowedTypes` to `disallowedElements`.
Show example of needed change
Before (**broken**):
```js
{``}
```
Now (**fixed**):
```js
{``}
```
***
Before (**broken**):
```js
node.type !== 'heading' || node.depth !== 1}
>{`# main heading`}
```
Now (**fixed**):
```js
element.tagName !== 'h1'}
>{`# main heading`}
```
### Change `includeNodeIndex` to `includeElementIndex`
Similar to the `renderers` to `components` change, this option to pass more info
to components also changed from being based on markdown to being based on HTML.
Show example of needed change
Before (**broken**):
```js
}}
>{`Some text`}
```
Now (**fixed**):
```js
}}
>{`Some text`}
```
### Change signature of `transformLinkUri`, `linkTarget`
The second parameter of these functions (to rewrite `href` on `a` or to define
`target` on `a`) are now [hast](https://github.com/syntax-tree/hast) (HTML AST)
instead of [mdast](https://github.com/syntax-tree/mdast) (markdown AST).
### Change signature of `transformImageUri`
The second parameter of this function was always `undefined` and the fourth was
the `alt` (`string`) on the image.
The second parameter is now that `alt`.
### Remove support for React 15, IE11
We now use ES2015 (such as `Object.assign`) and removed certain hacks to work
with React 15 and older.
## 5.0.3 - 2020-10-23
* [`bb0bdde`](https://github.com/remarkjs/react-markdown/commit/bb0bdde)
Unlock peer dependency on React to allow v17
* [`24e42bd`](https://github.com/remarkjs/react-markdown/commit/24e42bd)
Fix exception on missing element from `html-to-react`
* [`3d363e9`](https://github.com/remarkjs/react-markdown/commit/3d363e9)
Fix umd browser build
## 5.0.2 - 2020-10-23
* [`4dadaba`](https://github.com/remarkjs/react-markdown/commit/4dadaba)
Fix to allow combining `allowedTypes`, `unwrapDisallowed` in types
## 5.0.1 - 2020-10-21
* [`c3dc5ee`](https://github.com/remarkjs/react-markdown/commit/c3dc5ee)
Fix to not crash on empty text nodes
## 5.0.0 - 2020-10-19
### BREAKING
#### Maintained by [unified](https://unifiedjs.com)
This project is now maintained by the unified collective, which also houses the
underlying tools used in `react-markdown`: hundreds of projects for working with
markdown and markup related things (including MDX).
We have cleaned the project: updated dependencies, improved
docs/tests/coverage/types, cleaned the issue tracker, and fixed a couple of
bugs, but otherwise *much should be the same*.
#### Upgrade `remark-parse`
The parser used in `react-markdown` has been upgraded to the latest version.
It is now 100% CommonMark compliant: that means it works the same as in other
places, such as Discourse, Reddit, Stack Overflow, and GitHub.
Note that GitHub does extend CommonMark: to match how Markdown works on GitHub,
use the [`remark-gfm`](https://github.com/remarkjs/remark-gfm) plugin.
* [`remark-parse@9.0.0`](https://github.com/remarkjs/remark/releases/tag/remark-parse%409.0.0)
* [`remark-parse@8.0.0`](https://github.com/remarkjs/remark/releases/tag/remark-parse%408.0.0)
* [`remark-parse@7.0.0`](https://github.com/remarkjs/remark/releases/tag/remark-parse%407.0.0)
* [`remark-parse@6.0.0`](https://github.com/remarkjs/remark/releases/tag/remark-parse%406.0.0)
#### New serializer property: `node`
A new `node` prop is passed to all non-tag/non-fragment renderers.
This contains the raw [mdast](https://github.com/syntax-tree/mdast) AST node,
which opens up a number of interesting possibilities.
The breaking change is for renderers which blindly spread their props to an
underlying component/tag.
For instance:
```js
}} … />
```
Should now be written as:
```js
}} … />
```
#### List/list item `tight` property replaced by `spread`
Previously, the `tight` property would hint as to whether or not list items
should be wrapped in paragraphs.
This logic has now been replaced by a new `spread` property, which behaves
slightly differently.
[Read more](https://github.com/remarkjs/remark/pull/364).
## 4.3.1 - 2020-01-05
### Fixes
* (Typings) Fix incorrect typescript definitions (Peng Guanwen)
## 4.3.0 - 2020-01-02
### Fixes
* (Typings) Add typings for `react-markdown/html-parser` (Peng Guanwen)
## 4.2.2 - 2019-09-03
### Fixes
* (Typings) Inline `RemarkParseOptions` for now (Espen Hovlandsdal)
## 4.2.1 - 2019-09-01
### Fixes
* (Typings) Fix incorrect import - `RemarkParseOptions` (Jakub Chrzanowski)
## 4.2.0 - 2019-09-01
### Added
* Add support for plugins that use AST transformations (Frankie Ali)
### Fixes
* (Typings) Add `parserOptions` to type definitions (Ted Piotrowski)
* Allow renderer to be any React element type (Nathan Bierema)
## 4.1.0 - 2019-06-24
### Added
* Add prop `parserOptions` to specify options for remark-parse (Kelvin Chan)
## 4.0.9 - 2019-06-22
### Fixes
* (Typings) Make transformLinkUri & transformImageUri actually nullable
(Florentin Luca Rieger)
## 4.0.8 - 2019-04-14
### Fixes
* Fix HTML parsing of elements with a single child vs. multiple children
(Nicolas Venegas)
## 4.0.7 - 2019-04-14
### Fixes
* Fix matching of replaced non-void elements in HTML parser plugin (Nicolas
Venegas)
* Fix HTML parsing of multiple void elements (Nicolas Venegas)
* Fix void element children invariant violation (Nicolas Venegas)
## 4.0.6 - 2019-01-04
### Fixes
* Mitigate regex ddos by upgrading html-to-react (Christoph Werner)
* Update typings to allow arbitrary node types (Jesse Pinho)
* Readme: Add note about only parsing plugins working (Vincent Tunru)
## 4.0.4 - 2018-11-30
### Changed
* Upgrade dependencies (Espen Hovlandsdal)
## 4.0.3 - 2018-10-11
### Fixes
* Output paragraph element for last item in loose list (Jeremy Moseley)
## 4.0.2 - 2018-10-05
### Fixes
* Fix text rendering in React versions lower than or equal to 15 (Espen
Hovlandsdal)
## 4.0.1 - 2018-10-03
### Fixes
* \[TypeScript] Fix TypeScript index signature for renderers (Linus Unnebäck)
## 4.0.0 - 2018-10-03
### BREAKING
* `text` is now a first-class node + renderer
— if you are using `allowedNodes`, it needs to be included in this list.
Since it is now a React component, it will be passed an object of props
instead of the old approach where a string was passed.
`children` will contain the actual text string.
* On React >= 16.2, if no `className` prop is provided, a fragment will be
used instead of a div.
To always render a div, pass `'div'` as the `root` renderer.
* On React >= 16.2, escaped HTML will no longer be rendered with div/span
containers
* The UMD bundle now exports the component as `window.ReactMarkdown` instead
of `window.reactMarkdown`
### Added
* HTML parser plugin for full HTML compatibility (Espen Hovlandsdal)
### Fixes
* URI transformer allows uppercase http/https URLs (Liam Kennedy)
* \[TypeScript] Strongly type the keys of `renderers` (Linus Unnebäck)
## 3.6.0 - 2018-09-05
### Added
* Add support for passing index info to renderers (Beau Roberts)
## 3.5.0 - 2018-09-03
### Added
* Allow specifying `target` attribute for links (Marshall Smith)
## 3.4.1 - 2018-07-25
### Fixes
* Bump dependency for mdast-add-list-metadata as it was using ES6 features
(Espen Hovlandsdal)
## 3.4.0 - 2018-07-25
### Added
* Add more metadata props to list and listItem (André Staltz)
* list: `depth`
* listItem: `ordered`, `index`
### Fixes
* Make `source` property optional in typescript definition (gRoberts84)
## 3.3.4 - 2018-06-19
### Fixes
* Fix bug where rendering empty link references (`[][]`) would fail (Dennis S)
## 3.3.3 - 2018-06-14
### Fixes
* Fix bug where unwrapping certain disallowed nodes would fail (Petr Gazarov)
## 3.3.2 - 2018-05-07
### Changes
* Add `rawSourcePos` property for passing structured source position info to
renderers (Espen Hovlandsdal)
## 3.3.1 - 2018-05-07
### Changes
* Pass properties of unknown nodes directly to renderer (Jesse Pinho)
* Update TypeScript definition and prop types (ClassicDarkChocolate)
## 3.3.0 - 2018-03-06
### Added
* Add support for fragment renderers (Benjamim Sonntag)
## 3.2.2 - 2018-02-26
### Fixes
* Fix language escaping in code blocks (Espen Hovlandsdal)
## 3.2.1 - 2018-02-21
### Fixes
* Pass the React key into an overridden text renderer (vanchagreen)
## 3.2.0 - 2018-02-12
### Added
* Allow overriding text renderer (Thibaud Courtoison)
## 3.1.5 - 2018-02-03
### Fixes
* Only use first language from code block (Espen Hovlandsdal)
## 3.1.4 - 2017-12-30
### Fixes
* Enable transformImageUri for image references (evoye)
## 3.1.3 - 2017-12-16
### Fixes
* Exclude babel config from npm package (Espen Hovlandsdal)
## 3.1.2 - 2017-12-16
### Fixes
* Fixed partial table exception (Alexander Wong)
## 3.1.1 - 2017-12-11
### Fixes
* Add readOnly property to checkboxes (Phil Rajchgot)
## 3.1.0 - 2017-11-30
### Added
* Support for checkbox lists (Espen Hovlandsdal)
### Fixes
* Better typings (Igor Kamyshev)
## 3.0.1 - 2017-11-21
### Added
* *Experimental* support for plugins (Espen Hovlandsdal)
### Changes
* Provide more arguments to `transformLinkUri`/`transformImageUri` (children,
title, alt) (mudrz)
## 3.0.0 - 2017-11-20
### Notes
* **FULL REWRITE**.
Changed parser from CommonMark to Markdown.
Big, breaking changes.
See *BREAKING* below.
### Added
* Table support!
* New types: `table`, `tableHead`, `tableBody`, `tableRow`, `tableCell`
* New type: `delete` (`~~foo~~`)
* New type: `imageReference`
* New type: `linkReference`
* New type: `definition`
* Hacky, but basic support for React-native rendering of attributeless HTML
nodes (``, ``, etc)
### BREAKING
* Container props removed (`containerTagName`, `containerProps`), override
`root` renderer instead
* `softBreak` option removed.
New solution will be added at some point in the future.
* `escapeHtml` is now TRUE by default
* `HtmlInline`/`HtmlBlock` are now named `html` (use `isBlock` prop to check\
if inline or block)
* Renderer names are camelcased and in certain cases, renamed.
For instance:
* `Emph` => `emphasis`
* `Item` => `listItem`
* `Code` => `inlineCode`
* `CodeBlock` => `code`
* `linebreak`/`hardbreak` => `break`
* All renderers: `literal` prop is now called `value`\* List renderer: `type`
prop is now a boolean named `ordered` (`Bullet` => `false`, `Ordered` =>
`true`)
* `walker` prop removed.
Code depending on this will have to be rewritten to use the `astPlugins`
prop, which functions differently.
* `allowNode` has new arguments (node, index, parent)
— node has different props, see renderer props
* `childBefore` and `childAfter` props removed.
Use `root` renderer instead.
* `parserOptions` removed (new parser, so the old options doesn’t make sense
anymore)
## 2.5.1 - 2017-11-11
### Changes
* Fix ` ` not having a node key (Alex Zaworski)
## 2.5.0 - 2017-04-10
### Changes
* Fix deprecations for React v15.5 (Renée Kooi)
## 2.4.6 - 2017-03-14
### Changes
* Fix too strict TypeScript definition (Rasmus Eneman)
* Update JSON-loader info in readme to match webpack 2 (Robin Wieruch)
### Added
* Add ability to pass options to the CommonMark parser (Evan Hensleigh)
## 2.4.4 - 2017-01-16
### Changes
* Fixed TypeScript definitions (Kohei Asai)
## 2.4.3 - 2017-01-12
### Added
* Added TypeScript definitions (Ibragimov Ruslan)
## 2.4.2 - 2016-07-09
### Added
* Added UMD-build (`umd/react-markdown.js`) (Espen Hovlandsdal)
## 2.4.1 - 2016-07-09
### Changes
* Update `commonmark-react-renderer`, fixing a bug with missing nodes
(Espen Hovlandsdal)
## 2.4.0 - 2016-07-09
### Changes
* Plain DOM-node renderers are now given only their respective props.
Fixes warnings when using React >= 15.2 (Espen Hovlandsdal)
### Added
* New `transformImageUri` option allows you to transform URIs for images
(Petri Lehtinen)
## 2.3.0 - 2016-06-06
## Added
* The `walker` instance is now passed to the `walker` callback function
(Riku Rouvila)
## 2.2.0 - 2016-04-20
* Add `childBefore`/`childAfter` options (Thomas Lindstrøm)
## 2.1.1 - 2016-03-25
* Add `containerProps` option (Thomas Lindstrøm)
## 2.1.0 - 2016-03-12
### Changes
* Join sibling text nodes into one text node (Espen Hovlandsdal)
## 2.0.1 - 2016-02-21
### Changed
* Update `commonmark-react-renderer` dependency to latest version to add keys
to all elements and simplify custom renderers
## 2.0.0 - 2016-02-21
### Changed
* **Breaking change**: The renderer now requires Node 0.14 or higher.
This is because the renderer uses stateless components internally.
* **Breaking change**: `allowNode` now receives different properties in the
options argument.
See `README.md` for more details.
* **Breaking change**: CommonMark has changed some type names.
`Html` is now `HtmlInline`, `Header` is now `Heading` and `HorizontalRule`
is now `ThematicBreak`.
This affects the `allowedTypes` and `disallowedTypes` options.
* **Breaking change**: A bug in the `allowedTypes`/`disallowedTypes` and
`allowNode` options made them only applicable to certain types.
In this version, all types are filtered, as expected.
* **Breaking change**: Link URIs are now filtered through an XSS-filter by
default, prefixing “dangerous” protocols such as `javascript:` with `x-`
(eg: `javascript:alert('foo')` turns into `x-javascript:alert('foo')`).
This can be overridden with the `transformLinkUri`-option.
Pass `null` to disable the feature or a custom function to replace the
built-in behaviour.
### Added
* New `renderers` option allows you to customize which React component should
be used for rendering given types.
See `README.md` for more details.
(Espen Hovlandsdal / Guillaume Plique)
* New `unwrapDisallowed` option allows you to select if the contents of a
disallowed node should be “unwrapped” (placed into the disallowed node
position).
For instance, setting this option to true and disallowing a link would still
render the text of the link, instead of the whole link node and all it’s
children disappearing.
(Espen Hovlandsdal)
* New `transformLinkUri` option allows you to transform URIs in links.
By default, an XSS-filter is used, but you could also use this for use cases
like transforming absolute to relative URLs, or similar.
(Espen Hovlandsdal)
## 1.2.4 - 2016-01-28
### Changed
* Rolled back dependencies because of breaking changes
## 1.2.3 - 2016-01-24
### Changed
* Updated dependencies for both `commonmark` and `commonmark-react-parser` to
work around an embarrassing oversight on my part.
## 1.2.2 - 2016-01-08
### Changed
* Reverted change from 1.2.1 that uses the dist version.
Instead, documentation is added that specified the need for `json-loader` to
be enabled when using webpack.
## 1.2.1 - 2015-12-29
### Fixed
* Use pre-built (dist version) of commonmark renderer in order to work around
JSON-loader dependency.
## 1.2.0 - 2015-12-16
### Added
* Added new `allowNode`-property.
See README for details.
## 1.1.4 - 2015-12-14
### Fixed
* Set correct `libraryTarget` to make UMD builds work as expected
## 1.1.3 - 2015-12-14
### Fixed
* Update babel dependencies and run prepublish only as actual prepublish, not
install
## 1.1.1 - 2015-11-28
### Fixed
* Fixed issue with React external name in global environment (`react` vs `React`)
## 1.1.0 - 2015-11-22
### Changed
* Add ability to allow/disallow specific node types (`allowedTypes`/`disallowedTypes`)
## 1.0.5 - 2015-10-22
### Changed
* Moved React from dependency to peer dependency.
================================================
FILE: index.js
================================================
/**
* @typedef {import('./lib/index.js').AllowElement} AllowElement
* @typedef {import('./lib/index.js').Components} Components
* @typedef {import('./lib/index.js').ExtraProps} ExtraProps
* @typedef {import('./lib/index.js').HooksOptions} HooksOptions
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('./lib/index.js').UrlTransform} UrlTransform
*/
export {
MarkdownAsync,
MarkdownHooks,
Markdown as default,
defaultUrlTransform
} from './lib/index.js'
================================================
FILE: lib/index.js
================================================
/**
* @import {Element, Nodes, Parents, Root} from 'hast'
* @import {Root as MdastRoot} from 'mdast'
* @import {ComponentType, JSX, ReactElement, ReactNode} from 'react'
* @import {Options as RemarkRehypeOptions} from 'remark-rehype'
* @import {BuildVisitor} from 'unist-util-visit'
* @import {PluggableList, Processor} from 'unified'
*/
/**
* @callback AllowElement
* Filter elements.
* @param {Readonly} element
* Element to check.
* @param {number} index
* Index of `element` in `parent`.
* @param {Readonly | undefined} parent
* Parent of `element`.
* @returns {boolean | null | undefined}
* Whether to allow `element` (default: `false`).
*/
/**
* @typedef ExtraProps
* Extra fields we pass.
* @property {Element | undefined} [node]
* passed when `passNode` is on.
*/
/**
* @typedef {{
* [Key in keyof JSX.IntrinsicElements]?: ComponentType | keyof JSX.IntrinsicElements
* }} Components
* Map tag names to components.
*/
/**
* @typedef Deprecation
* Deprecation.
* @property {string} from
* Old field.
* @property {string} id
* ID in readme.
* @property {keyof Options} [to]
* New field.
*/
/**
* @typedef Options
* Configuration.
* @property {AllowElement | null | undefined} [allowElement]
* Filter elements (optional);
* `allowedElements` / `disallowedElements` is used first.
* @property {ReadonlyArray | null | undefined} [allowedElements]
* Tag names to allow (default: all tag names);
* cannot combine w/ `disallowedElements`.
* @property {string | null | undefined} [children]
* Markdown.
* @property {Components | null | undefined} [components]
* Map tag names to components.
* @property {ReadonlyArray | null | undefined} [disallowedElements]
* Tag names to disallow (default: `[]`);
* cannot combine w/ `allowedElements`.
* @property {PluggableList | null | undefined} [rehypePlugins]
* List of rehype plugins to use.
* @property {PluggableList | null | undefined} [remarkPlugins]
* List of remark plugins to use.
* @property {Readonly | null | undefined} [remarkRehypeOptions]
* Options to pass through to `remark-rehype`.
* @property {boolean | null | undefined} [skipHtml=false]
* Ignore HTML in markdown completely (default: `false`).
* @property {boolean | null | undefined} [unwrapDisallowed=false]
* Extract (unwrap) what’s in disallowed elements (default: `false`);
* normally when say `strong` is not allowed, it and it’s children are dropped,
* with `unwrapDisallowed` the element itself is replaced by its children.
* @property {UrlTransform | null | undefined} [urlTransform]
* Change URLs (default: `defaultUrlTransform`)
*/
/**
* @typedef HooksOptionsOnly
* Configuration specifically for {@linkcode MarkdownHooks}.
* @property {ReactNode | null | undefined} [fallback]
* Content to render while the processor processing the markdown (optional).
*/
/**
* @typedef {Options & HooksOptionsOnly} HooksOptions
* Configuration for {@linkcode MarkdownHooks};
* extends the regular {@linkcode Options} with a `fallback` prop.
*/
/**
* @callback UrlTransform
* Transform all URLs.
* @param {string} url
* URL.
* @param {string} key
* Property name (example: `'href'`).
* @param {Readonly} node
* Node.
* @returns {string | null | undefined}
* Transformed URL (optional).
*/
import {unreachable} from 'devlop'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {urlAttributes} from 'html-url-attributes'
import {Fragment, jsx, jsxs} from 'react/jsx-runtime'
import {useEffect, useMemo, useState} from 'react'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import {unified} from 'unified'
import {visit} from 'unist-util-visit'
import {VFile} from 'vfile'
const changelog =
'https://github.com/remarkjs/react-markdown/blob/main/changelog.md'
/** @type {PluggableList} */
const emptyPlugins = []
/** @type {Readonly} */
const emptyRemarkRehypeOptions = {allowDangerousHtml: true}
const safeProtocol = /^(https?|ircs?|mailto|xmpp)$/i
// Mutable because we `delete` any time it’s used and a message is sent.
/** @type {ReadonlyArray>} */
const deprecations = [
{from: 'astPlugins', id: 'remove-buggy-html-in-markdown-parser'},
{from: 'allowDangerousHtml', id: 'remove-buggy-html-in-markdown-parser'},
{
from: 'allowNode',
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
to: 'allowElement'
},
{
from: 'allowedTypes',
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
to: 'allowedElements'
},
{from: 'className', id: 'remove-classname'},
{
from: 'disallowedTypes',
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
to: 'disallowedElements'
},
{from: 'escapeHtml', id: 'remove-buggy-html-in-markdown-parser'},
{from: 'includeElementIndex', id: '#remove-includeelementindex'},
{
from: 'includeNodeIndex',
id: 'change-includenodeindex-to-includeelementindex'
},
{from: 'linkTarget', id: 'remove-linktarget'},
{from: 'plugins', id: 'change-plugins-to-remarkplugins', to: 'remarkPlugins'},
{from: 'rawSourcePos', id: '#remove-rawsourcepos'},
{from: 'renderers', id: 'change-renderers-to-components', to: 'components'},
{from: 'source', id: 'change-source-to-children', to: 'children'},
{from: 'sourcePos', id: '#remove-sourcepos'},
{from: 'transformImageUri', id: '#add-urltransform', to: 'urlTransform'},
{from: 'transformLinkUri', id: '#add-urltransform', to: 'urlTransform'}
]
/**
* Component to render markdown.
*
* This is a synchronous component.
* When using async plugins,
* see {@linkcode MarkdownAsync} or {@linkcode MarkdownHooks}.
*
* @param {Readonly} options
* Props.
* @returns {ReactElement}
* React element.
*/
export function Markdown(options) {
const processor = createProcessor(options)
const file = createFile(options)
return post(processor.runSync(processor.parse(file), file), options)
}
/**
* Component to render markdown with support for async plugins
* through async/await.
*
* Components returning promises are supported on the server.
* For async support on the client,
* see {@linkcode MarkdownHooks}.
*
* @param {Readonly} options
* Props.
* @returns {Promise}
* Promise to a React element.
*/
export async function MarkdownAsync(options) {
const processor = createProcessor(options)
const file = createFile(options)
const tree = await processor.run(processor.parse(file), file)
return post(tree, options)
}
/**
* Component to render markdown with support for async plugins through hooks.
*
* This uses `useEffect` and `useState` hooks.
* Hooks run on the client and do not immediately render something.
* For async support on the server,
* see {@linkcode MarkdownAsync}.
*
* @param {Readonly} options
* Props.
* @returns {ReactNode}
* React node.
*/
export function MarkdownHooks(options) {
const processor = useMemo(
function () {
return createProcessor(options)
},
[options.rehypePlugins, options.remarkPlugins, options.remarkRehypeOptions]
)
const [error, setError] = useState(
/** @type {Error | undefined} */ (undefined)
)
const [tree, setTree] = useState(/** @type {Root | undefined} */ (undefined))
useEffect(
function () {
let cancelled = false
const file = createFile(options)
processor.run(processor.parse(file), file, function (error, tree) {
if (!cancelled) {
setError(error)
setTree(tree)
}
})
/**
* @returns {undefined}
* Nothing.
*/
return function () {
cancelled = true
}
},
[options.children, processor]
)
if (error) throw error
return tree ? post(tree, options) : options.fallback
}
/**
* Set up the `unified` processor.
*
* @param {Readonly} options
* Props.
* @returns {Processor}
* Result.
*/
function createProcessor(options) {
const rehypePlugins = options.rehypePlugins || emptyPlugins
const remarkPlugins = options.remarkPlugins || emptyPlugins
const remarkRehypeOptions = options.remarkRehypeOptions
? {...options.remarkRehypeOptions, ...emptyRemarkRehypeOptions}
: emptyRemarkRehypeOptions
const processor = unified()
.use(remarkParse)
.use(remarkPlugins)
.use(remarkRehype, remarkRehypeOptions)
.use(rehypePlugins)
return processor
}
/**
* Set up the virtual file.
*
* @param {Readonly} options
* Props.
* @returns {VFile}
* Result.
*/
function createFile(options) {
const children = options.children || ''
const file = new VFile()
if (typeof children === 'string') {
file.value = children
} else {
unreachable(
'Unexpected value `' +
children +
'` for `children` prop, expected `string`'
)
}
return file
}
/**
* Process the result from unified some more.
*
* @param {Nodes} tree
* Tree.
* @param {Readonly} options
* Props.
* @returns {ReactElement}
* React element.
*/
function post(tree, options) {
const allowedElements = options.allowedElements
const allowElement = options.allowElement
const components = options.components
const disallowedElements = options.disallowedElements
const skipHtml = options.skipHtml
const unwrapDisallowed = options.unwrapDisallowed
const urlTransform = options.urlTransform || defaultUrlTransform
for (const deprecation of deprecations) {
if (Object.hasOwn(options, deprecation.from)) {
unreachable(
'Unexpected `' +
deprecation.from +
'` prop, ' +
(deprecation.to
? 'use `' + deprecation.to + '` instead'
: 'remove it') +
' (see <' +
changelog +
'#' +
deprecation.id +
'> for more info)'
)
}
}
if (allowedElements && disallowedElements) {
unreachable(
'Unexpected combined `allowedElements` and `disallowedElements`, expected one or the other'
)
}
visit(tree, transform)
return toJsxRuntime(tree, {
Fragment,
components,
ignoreInvalidStyle: true,
jsx,
jsxs,
passKeys: true,
passNode: true
})
/** @type {BuildVisitor} */
function transform(node, index, parent) {
if (node.type === 'raw' && parent && typeof index === 'number') {
if (skipHtml) {
parent.children.splice(index, 1)
} else {
parent.children[index] = {type: 'text', value: node.value}
}
return index
}
if (node.type === 'element') {
/** @type {string} */
let key
for (key in urlAttributes) {
if (
Object.hasOwn(urlAttributes, key) &&
Object.hasOwn(node.properties, key)
) {
const value = node.properties[key]
const test = urlAttributes[key]
if (test === null || test.includes(node.tagName)) {
node.properties[key] = urlTransform(String(value || ''), key, node)
}
}
}
}
if (node.type === 'element') {
let remove = allowedElements
? !allowedElements.includes(node.tagName)
: disallowedElements
? disallowedElements.includes(node.tagName)
: false
if (!remove && allowElement && typeof index === 'number') {
remove = !allowElement(node, index, parent)
}
if (remove && parent && typeof index === 'number') {
if (unwrapDisallowed && node.children) {
parent.children.splice(index, 1, ...node.children)
} else {
parent.children.splice(index, 1)
}
return index
}
}
}
}
/**
* Make a URL safe.
*
* This follows how GitHub works.
* It allows the protocols `http`, `https`, `irc`, `ircs`, `mailto`, and `xmpp`,
* and URLs relative to the current protocol (such as `/something`).
*
* @satisfies {UrlTransform}
* @param {string} value
* URL.
* @returns {string}
* Safe URL.
*/
export function defaultUrlTransform(value) {
// Same as:
//
// But without the `encode` part.
const colon = value.indexOf(':')
const questionMark = value.indexOf('?')
const numberSign = value.indexOf('#')
const slash = value.indexOf('/')
if (
// If there is no protocol, it’s relative.
colon === -1 ||
// If the first colon is after a `?`, `#`, or `/`, it’s not a protocol.
(slash !== -1 && colon > slash) ||
(questionMark !== -1 && colon > questionMark) ||
(numberSign !== -1 && colon > numberSign) ||
// It is a protocol, it should be allowed.
safeProtocol.test(value.slice(0, colon))
) {
return value
}
return ''
}
================================================
FILE: license
================================================
The MIT License (MIT)
Copyright (c) Espen Hovlandsdal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: package.json
================================================
{
"author": "Espen Hovlandsdal ",
"bugs": "https://github.com/remarkjs/react-markdown/issues",
"contributors": [
"Alexander Wallin ",
"Alexander Wong ",
"André Staltz ",
"Angus MacIsaac ",
"Beau Roberts ",
"Charlie Chen ",
"Christian Murphy ",
"Christoph Werner ",
"Danny ",
"Dennis S ",
"Espen Hovlandsdal ",
"Evan Hensleigh ",
"Fabian Irsara ",
"Florentin Luca Rieger ",
"Frank ",
"Igor Kamyshev ",
"Jack Williams ",
"Jakub Chrzanowski ",
"Jeremy Moseley ",
"Jesse Pinho ",
"Kelvin Chan ",
"Kohei Asai ",
"Linus Unnebäck ",
"Marshall Smith ",
"Nathan Bierema ",
"Nicolas Venegas ",
"Peng Guanwen ",
"Petr Gazarov ",
"Phil Rajchgot ",
"Rasmus Eneman ",
"René Kooi ",
"Riku Rouvila ",
"Robin Wieruch ",
"Rostyslav Melnychuk ",
"Ted Piotrowski ",
"Thibaud Courtoison ",
"Thomas Lindstrøm ",
"Tiago Roldão ",
"Titus Wormer (https://wooorm.com)",
"cerkiewny ",
"evoye ",
"gRoberts84 ",
"mudrz ",
"vanchagreen "
],
"dependencies": {
"@types/hast": "^3.0.0",
"@types/mdast": "^4.0.0",
"devlop": "^1.0.0",
"hast-util-to-jsx-runtime": "^2.0.0",
"html-url-attributes": "^3.0.0",
"mdast-util-to-hast": "^13.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.0.0",
"unified": "^11.0.0",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.0"
},
"description": "React component to render markdown",
"devDependencies": {
"@testing-library/react": "^16.0.0",
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"c8": "^10.0.0",
"concat-stream": "^2.0.0",
"esbuild": "^0.25.0",
"eslint-plugin-react": "^7.0.0",
"global-jsdom": "^26.0.0",
"prettier": "^3.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rehype-raw": "^7.0.0",
"rehype-starry-night": "^2.0.0",
"remark-cli": "^12.0.0",
"remark-gfm": "^4.0.0",
"remark-preset-wooorm": "^11.0.0",
"remark-toc": "^9.0.0",
"type-coverage": "^2.0.0",
"typescript": "^5.0.0",
"xo": "^0.60.0"
},
"exports": "./index.js",
"files": [
"index.d.ts.map",
"index.d.ts",
"index.js",
"lib/"
],
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
},
"keywords": [
"ast",
"commonmark",
"component",
"gfm",
"markdown",
"react",
"react-component",
"remark",
"unified"
],
"license": "MIT",
"name": "react-markdown",
"peerDependencies": {
"@types/react": ">=18",
"react": ">=18"
},
"prettier": {
"bracketSpacing": false,
"singleQuote": true,
"semi": false,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false
},
"remarkConfig": {
"plugins": [
"remark-preset-wooorm",
[
"remark-lint-no-html",
false
]
]
},
"repository": "remarkjs/react-markdown",
"scripts": {
"build": "tsc --build --clean && tsc --build && type-coverage",
"format": "remark --frail --output --quiet -- . && prettier --log-level warn --write -- . && xo --fix",
"test-api": "node --conditions development --experimental-loader=./script/load-jsx.js --no-warnings test.jsx",
"test-coverage": "c8 --100 --exclude script/ --reporter lcov -- npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"
},
"sideEffects": false,
"typeCoverage": {
"atLeast": 100,
"strict": true
},
"type": "module",
"version": "10.1.0",
"xo": {
"envs": [
"shared-node-browser"
],
"extends": "plugin:react/jsx-runtime",
"overrides": [
{
"files": [
"**/*.jsx"
],
"rules": {
"no-unused-vars": "off"
}
}
],
"prettier": true,
"rules": {
"complexity": "off",
"n/file-extension-in-import": "off",
"unicorn/prevent-abbreviations": "off"
}
}
}
================================================
FILE: readme.md
================================================
# react-markdown
[![Build][badge-build-image]][badge-build-url]
[![Coverage][badge-coverage-image]][badge-coverage-url]
[![Downloads][badge-downloads-image]][badge-downloads-url]
[![Size][badge-size-image]][badge-size-url]
React component to render markdown.
## Feature highlights
* [x] **[safe][section-security] by default**
(no `dangerouslySetInnerHTML` or XSS attacks)
* [x] **[components][section-components]**
(pass your own component to use instead of `
` for `## hi`)
* [x] **[plugins][section-plugins]**
(many plugins you can pick and choose from)
* [x] **[compliant][section-syntax]**
(100% to CommonMark, 100% to GFM with a plugin)
## Contents
* [What is this?](#what-is-this)
* [When should I use this?](#when-should-i-use-this)
* [Install](#install)
* [Use](#use)
* [API](#api)
* [`Markdown`](#markdown)
* [`MarkdownAsync`](#markdownasync)
* [`MarkdownHooks`](#markdownhooks)
* [`defaultUrlTransform(url)`](#defaulturltransformurl)
* [`AllowElement`](#allowelement)
* [`Components`](#components)
* [`ExtraProps`](#extraprops)
* [`HooksOptions`](#hooksoptions)
* [`Options`](#options)
* [`UrlTransform`](#urltransform)
* [Examples](#examples)
* [Use a plugin](#use-a-plugin)
* [Use a plugin with options](#use-a-plugin-with-options)
* [Use custom components (syntax highlight)](#use-custom-components-syntax-highlight)
* [Use remark and rehype plugins (math)](#use-remark-and-rehype-plugins-math)
* [Plugins](#plugins)
* [Syntax](#syntax)
* [Compatibility](#compatibility)
* [Architecture](#architecture)
* [Appendix A: HTML in markdown](#appendix-a-html-in-markdown)
* [Appendix B: Components](#appendix-b-components)
* [Appendix C: line endings in markdown (and JSX)](#appendix-c-line-endings-in-markdown-and-jsx)
* [Security](#security)
* [Related](#related)
* [Contribute](#contribute)
* [License](#license)
## What is this?
This package is a [React][] component that can be given a string of markdown
that it’ll safely render to React elements.
You can pass plugins to change how markdown is transformed and pass components
that will be used instead of normal HTML elements.
* to learn markdown, see this [cheatsheet and tutorial][commonmark-help]
* to try out `react-markdown`, see [our demo][github-io-react-markdown]
## When should I use this?
There are other ways to use markdown in React out there so why use this one?
The three main reasons are that they often rely on `dangerouslySetInnerHTML`,
have bugs with how they handle markdown, or don’t let you swap elements for
components.
`react-markdown` builds a virtual DOM, so React only replaces what changed,
from a syntax tree.
That’s supported because we use [unified][github-unified],
specifically [remark][github-remark] for markdown and [rehype][github-rehype]
for HTML,
which are popular tools to transform content with plugins.
This package focusses on making it easy for beginners to safely use markdown in
React.
When you’re familiar with unified, you can use a modern hooks based alternative
[`react-remark`][github-react-remark] or [`rehype-react`][github-rehype-react]
manually.
If you instead want to use JavaScript and JSX *inside* markdown files, use
[MDX][github-mdx].
## Install
This package is [ESM only][esm].
In Node.js (version 16+), install with [npm][npm-install]:
```sh
npm install react-markdown
```
In Deno with [`esm.sh`][esmsh]:
```js
import Markdown from 'https://esm.sh/react-markdown@10'
```
In browsers with [`esm.sh`][esmsh]:
```html
```
## Use
A basic hello world:
```js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
const markdown = '# Hi, *Pluto*!'
createRoot(document.body).render({markdown})
```
Show equivalent JSX
```js
Hi, Pluto!
```
Here is an example that shows how to use a plugin
([`remark-gfm`][github-remark-gfm],
which adds support for footnotes, strikethrough, tables, tasklists and
URLs directly):
```js
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}
)
```
Show equivalent JSX
```js
```
## API
This package exports the identifiers
[`MarkdownAsync`][api-markdown-async],
[`MarkdownHooks`][api-markdown-hooks],
and
[`defaultUrlTransform`][api-default-url-transform].
The default export is [`Markdown`][api-markdown].
It also exports the additional [TypeScript][] types
[`AllowElement`][api-allow-element],
[`Components`][api-components],
[`ExtraProps`][api-extra-props],
[`HooksOptions`][api-hooks-options],
[`Options`][api-options],
and
[`UrlTransform`][api-url-transform].
### `Markdown`
Component to render markdown.
This is a synchronous component.
When using async plugins,
see [`MarkdownAsync`][api-markdown-async] or
[`MarkdownHooks`][api-markdown-hooks].
###### Parameters
* `options` ([`Options`][api-options])
— props
###### Returns
React element (`ReactElement`).
### `MarkdownAsync`
Component to render markdown with support for async plugins
through async/await.
Components returning promises are supported on the server.
For async support on the client,
see [`MarkdownHooks`][api-markdown-hooks].
###### Parameters
* `options` ([`Options`][api-options])
— props
###### Returns
Promise to a React element (`Promise`).
### `MarkdownHooks`
Component to render markdown with support for async plugins through hooks.
This uses `useEffect` and `useState` hooks.
Hooks run on the client and do not immediately render something.
For async support on the server,
see [`MarkdownAsync`][api-markdown-async].
###### Parameters
* `options` ([`Options`][api-options])
— props
###### Returns
React node (`ReactNode`).
### `defaultUrlTransform(url)`
Make a URL safe.
This follows how GitHub works.
It allows the protocols `http`, `https`, `irc`, `ircs`, `mailto`, and `xmpp`,
and URLs relative to the current protocol (such as `/something`).
###### Parameters
* `url` (`string`)
— URL
###### Returns
Safe URL (`string`).
### `AllowElement`
Filter elements (TypeScript type).
###### Parameters
* `node` ([`Element` from `hast`][github-hast-element])
— element to check
* `index` (`number | undefined`)
— index of `element` in `parent`
* `parent` ([`Node` from `hast`][github-hast-nodes])
— parent of `element`
###### Returns
Whether to allow `element` (`boolean`, optional).
### `Components`
Map tag names to components (TypeScript type).
###### Type
```ts
import type {ExtraProps} from 'react-markdown'
import type {ComponentProps, ElementType} from 'react'
type Components = {
[Key in Extract]?: ElementType & ExtraProps>
}
```
### `ExtraProps`
Extra fields we pass to components (TypeScript type).
###### Fields
* `node` ([`Element` from `hast`][github-hast-element], optional)
— original node
### `HooksOptions`
Configuration for [`MarkdownHooks`][api-markdown-hooks] (TypeScript type);
extends the regular [`Options`][api-options] with a `fallback` prop.
###### Extends
[`Options`][api-options].
###### Fields
* `fallback` (`ReactNode`, optional)
— content to render while the processor processing the markdown
### `Options`
Configuration (TypeScript type).
###### Fields
* `allowElement` ([`AllowElement`][api-allow-element], optional)
— filter elements;
`allowedElements` / `disallowedElements` is used first
* `allowedElements` (`Array`, default: all tag names)
— tag names to allow;
cannot combine w/ `disallowedElements`
* `children` (`string`, optional)
— markdown
* `components` ([`Components`][api-components], optional)
— map tag names to components
* `disallowedElements` (`Array`, default: `[]`)
— tag names to disallow;
cannot combine w/ `allowedElements`
* `rehypePlugins` (`Array`, optional)
— list of [rehype plugins][github-rehype-plugins] to use
* `remarkPlugins` (`Array`, optional)
— list of [remark plugins][github-remark-plugins] to use
* `remarkRehypeOptions`
([`Options` from `remark-rehype`][github-remark-rehype-options],
optional)
— options to pass through to `remark-rehype`
* `skipHtml` (`boolean`, default: `false`)
— ignore HTML in markdown completely
* `unwrapDisallowed` (`boolean`, default: `false`)
— extract (unwrap) what’s in disallowed elements;
normally when say `strong` is not allowed, it and it’s children are dropped,
with `unwrapDisallowed` the element itself is replaced by its children
* `urlTransform` ([`UrlTransform`][api-url-transform], default:
[`defaultUrlTransform`][api-default-url-transform])
— change URLs
### `UrlTransform`
Transform URLs (TypeScript type).
###### Parameters
* `url` (`string`)
— URL
* `key` (`string`, example: `'href'`)
— property name
* `node` ([`Element` from `hast`][github-hast-element])
— element to check
###### Returns
Transformed URL (`string`, optional).
## Examples
### Use a plugin
This example shows how to use a remark plugin.
In this case, [`remark-gfm`][github-remark-gfm],
which adds support for strikethrough, tables, tasklists and URLs directly:
```js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
const markdown = `A paragraph with *emphasis* and **strong importance**.
> A block quote with ~strikethrough~ and a URL: https://reactjs.org.
* Lists
* [ ] todo
* [x] done
A table:
| a | b |
| - | - |
`
createRoot(document.body).render(
{markdown}
)
```
Show equivalent JSX
```js
<>
>
```
### Use a plugin with options
This example shows how to use a plugin and give it options.
To do that, use an array with the plugin at the first place, and the options
second.
[`remark-gfm`][github-remark-gfm] has an option to allow only double tildes for
strikethrough:
```js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
const markdown = 'This ~is not~ strikethrough, but ~~this is~~!'
createRoot(document.body).render(
{markdown}
)
```
Show equivalent JSX
```js
This ~is not~ strikethrough, but this is!
```
### Use custom components (syntax highlight)
This example shows how you can overwrite the normal handling of an element by
passing a component.
In this case, we apply syntax highlighting with the seriously super amazing
[`react-syntax-highlighter`][github-react-syntax-highlighter] by
[**@conorhastings**][github-conorhastings]:
```js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
import {dark} from 'react-syntax-highlighter/dist/esm/styles/prism'
// Did you know you can use tildes instead of backticks for code in markdown? ✨
const markdown = `Here is some JavaScript code:
~~~js
console.log('It works!')
~~~
`
createRoot(document.body).render(
) : (
{children}
)
}
}}
/>
)
```
Show equivalent JSX
```js
<>
Here is some JavaScript code:
>
```
### Use remark and rehype plugins (math)
This example shows how a syntax extension
(through [`remark-math`][github-remark-math])
is used to support math in markdown, and a transform plugin
([`rehype-katex`][github-rehype-katex]) to render that math.
```js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import rehypeKatex from 'rehype-katex'
import remarkMath from 'remark-math'
import 'katex/dist/katex.min.css' // `rehype-katex` does not import the CSS for you
const markdown = `The lift coefficient ($C_L$) is a dimensionless coefficient.`
createRoot(document.body).render(
{markdown}
)
```
Show equivalent JSX
```js
The lift coefficient (
{/* … */}
) is a dimensionless coefficient.
```
## Plugins
We use [unified][github-unified],
specifically [remark][github-remark] for markdown and
[rehype][github-rehype] for HTML,
which are tools to transform content with plugins.
Here are three good ways to find plugins:
* [`awesome-remark`][github-awesome-remark] and
[`awesome-rehype`][github-awesome-rehype]
— selection of the most awesome projects
* [List of remark plugins][github-remark-plugins] and
[list of rehype plugins][github-rehype-plugins]
— list of all plugins
* [`remark-plugin`][github-topic-remark-plugin] and
[`rehype-plugin`][github-topic-rehype-plugin] topics
— any tagged repo on GitHub
## Syntax
`react-markdown` follows CommonMark, which standardizes the differences between
markdown implementations, by default.
Some syntax extensions are supported through plugins.
We use [`micromark`][github-micromark] under the hood for our parsing.
See its documentation for more information on markdown, CommonMark, and
extensions.
## Compatibility
Projects maintained by the unified collective are compatible with maintained
versions of Node.js.
When we cut a new major release, we drop support for unmaintained versions of
Node.
This means we try to keep the current release line, `react-markdown@10`,
compatible with Node.js 16.
They work in all modern browsers (essentially: everything not IE 11).
You can use a bundler (such as esbuild, webpack, or Rollup) to use this package
in your project, and use its options (or plugins) to add support for legacy
browsers.
## Architecture
To understand what this project does, it’s important to first understand what
unified does: please read through the [`unifiedjs/unified`][github-unified]
readme
(the part until you hit the API section is required reading).
`react-markdown` is a unified pipeline — wrapped so that most folks don’t need
to directly interact with unified.
The processor goes through these steps:
* parse markdown to mdast (markdown syntax tree)
* transform through remark (markdown ecosystem)
* transform mdast to hast (HTML syntax tree)
* transform through rehype (HTML ecosystem)
* render hast to React with components
## Appendix A: HTML in markdown
`react-markdown` typically escapes HTML (or ignores it, with `skipHtml`)
because it is dangerous and defeats the purpose of this library.
However, if you are in a trusted environment (you trust the markdown), and
can spare the bundle size (±60kb minzipped), then you can use
[`rehype-raw`][github-rehype-raw]:
```js
import React from 'react'
import {createRoot} from 'react-dom/client'
import Markdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
const markdown = `
Some *emphasis* and strong!
`
createRoot(document.body).render(
{markdown}
)
```
Show equivalent JSX
```js
'
)
})
await t.test('should support a list (unordered) / list item', function () {
assert.equal(
renderToStaticMarkup(),
'
\n
a
\n
'
)
})
await t.test('should support a list (ordered) / list item', function () {
assert.equal(
renderToStaticMarkup(),
'\n
a
\n'
)
})
await t.test('should support a paragraph', function () {
assert.equal(renderToStaticMarkup(), '
a
')
})
await t.test('should support a strong', function () {
assert.equal(
renderToStaticMarkup(),
'
a
'
)
})
await t.test('should support a table (GFM)', function () {
assert.equal(
renderToStaticMarkup(
),
'
a
b
'
)
})
await t.test('should support a table (GFM; w/ align)', function () {
assert.equal(
renderToStaticMarkup(
),
'
a
b
c
d
'
)
})
await t.test('should support a thematic break', function () {
assert.equal(renderToStaticMarkup(), '')
})
await t.test('should support ab absolute path', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should support an absolute URL', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should support a URL w/ uppercase protocol', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should make a `javascript:` URL safe', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should make a `vbscript:` URL safe', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should make a `VBSCRIPT:` URL safe', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should make a `file:` URL safe', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should allow an empty URL', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should support search (`?`) in a URL', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should support hash (`&`) in a URL', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should support hash (`#`) in a URL', function () {
assert.equal(
renderToStaticMarkup(),
'
'
)
})
await t.test('should support `urlTransform` (`href` on `a`)', function () {
assert.equal(
renderToStaticMarkup(
),
'
'
)
})
await t.test('should support `urlTransform` w/ empty URLs', function () {
assert.equal(
renderToStaticMarkup(
),
'
'
)
})
await t.test('should support `urlTransform` (`src` on `img`)', function () {
assert.equal(
renderToStaticMarkup(
),
'
'
)
})
await t.test('should support `skipHtml`', function () {
const actual = renderToStaticMarkup(
)
assert.equal(actual, '
abc
')
})
await t.test(
'should support `allowedElements` (drop unlisted nodes)',
function () {
assert.equal(
renderToStaticMarkup(
),
'\n
\n
b
\n
'
)
}
)
await t.test('should support `allowedElements` as a function', function () {
assert.equal(
renderToStaticMarkup(
),
'
b
'
)
})
await t.test('should support `disallowedElements`', function () {
assert.equal(
renderToStaticMarkup(
),
'\n
\n
b
\n
'
)
})
await t.test(
'should fail for both `allowedElements` and `disallowedElements`',
function () {
assert.throws(function () {
renderToStaticMarkup(
)
}, /Unexpected combined `allowedElements` and `disallowedElements`, expected one or the other/)
}
)
await t.test(
'should support `unwrapDisallowed` w/ `allowedElements`',
function () {
assert.equal(
renderToStaticMarkup(
),
'
a
'
)
}
)
await t.test(
'should support `unwrapDisallowed` w/ `disallowedElements`',
function () {
assert.equal(
renderToStaticMarkup(
),
'
a
'
)
}
)
await t.test('should support `remarkRehypeOptions`', function () {
assert.equal(
renderToStaticMarkup(
),
'
\n\n'
)
})
await t.test('should support `components`', function () {
assert.equal(
renderToStaticMarkup(),
'
a
'
)
})
await t.test('should support `components` as functions', function () {
assert.equal(
renderToStaticMarkup(
}
}}
/>
),
'
a
'
)
})
await t.test('should fail on an invalid component', function () {
assert.throws(function () {
renderToStaticMarkup(
)
}, /Element type is invalid/)
})
await t.test('should support `components` (headings)', function () {
let calls = 0
assert.equal(
renderToStaticMarkup(
),
'