html-react-parser
Version:
HTML to React parser.
524 lines (378 loc) • 16.8 kB
Markdown
# html-react-parser
[](https://nodei.co/npm/html-react-parser/)
[](https://www.npmjs.com/package/html-react-parser)
[](https://github.com/remarkablemark/html-react-parser/actions?query=workflow%3Abuild)
[](https://coveralls.io/github/remarkablemark/html-react-parser?branch=master)
[](https://david-dm.org/remarkablemark/html-react-parser)
[](https://www.npmjs.com/package/html-react-parser)
[](https://discord.gg/njExwXdrRJ)
HTML to React parser that works on both the server (Node.js) and the client (browser):
```
HTMLReactParser(string[, options])
```
The parser converts an HTML string to one or more [React elements](https://reactjs.org/docs/react-api.html#creating-react-elements).
To replace an element with another element, check out the [`replace`](#replace) option.
#### Example
```js
const parse = require('html-react-parser');
parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')
```
[Repl.it](https://repl.it/@remarkablemark/html-react-parser) | [JSFiddle](https://jsfiddle.net/remarkablemark/7v86d800/) | [CodeSandbox](https://codesandbox.io/s/940pov1l4w) | [TypeScript](https://codesandbox.io/s/html-react-parser-z0kp6) | [Examples](https://github.com/remarkablemark/html-react-parser/tree/master/examples)
<details>
<summary>Table of Contents</summary>
- [Install](#install)
- [Usage](#usage)
- [replace](#replace)
- [library](#library)
- [htmlparser2](#htmlparser2)
- [trim](#trim)
- [Migration](#migration)
- [v1.0.0](#v100)
- [FAQ](#faq)
- [Is this XSS safe?](#is-this-xss-safe)
- [Does invalid HTML get sanitized?](#does-invalid-html-get-sanitized)
- [Are `<script>` tags parsed?](#are-script-tags-parsed)
- [Attributes aren't getting called](#attributes-arent-getting-called)
- [Parser throws an error](#parser-throws-an-error)
- [Is SSR supported?](#is-ssr-supported)
- [Elements aren't nested correctly](#elements-arent-nested-correctly)
- [Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of table](#warning-validatedomnesting-whitespace-text-nodes-cannot-appear-as-a-child-of-table)
- [Don't change case of tags](#dont-change-case-of-tags)
- [TS Error: Property 'attribs' does not exist on type 'DOMNode'](#ts-error-property-attribs-does-not-exist-on-type-domnode)
- [Can I enable `trim` for certain elements?](#can-i-enable-trim-for-certain-elements)
- [Performance](#performance)
- [Contributors](#contributors)
- [Code Contributors](#code-contributors)
- [Financial Contributors](#financial-contributors)
- [Individuals](#individuals)
- [Organizations](#organizations)
- [Support](#support)
- [License](#license)
</details>
## Install
[NPM](https://www.npmjs.com/package/html-react-parser):
```sh
$ npm install html-react-parser --save
```
[Yarn](https://yarnpkg.com/package/html-react-parser):
```sh
$ yarn add html-react-parser
```
[CDN](https://unpkg.com/html-react-parser/):
```html
<!-- HTMLReactParser depends on React -->
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/html-react-parser@latest/dist/html-react-parser.min.js"></script>
<script>
window.HTMLReactParser(/* string */);
</script>
```
## Usage
Import or require the module:
```js
// ES Modules
import parse from 'html-react-parser';
// CommonJS
const parse = require('html-react-parser');
```
Parse single element:
```js
parse('<h1>single</h1>');
```
Parse multiple elements:
```js
parse('<li>Item 1</li><li>Item 2</li>');
```
Make sure to render parsed adjacent elements under a parent element:
```jsx
<ul>
{parse(`
<li>Item 1</li>
<li>Item 2</li>
`)}
</ul>
```
Parse nested elements:
```js
parse('<body><p>Lorem ipsum</p></body>');
```
Parse element with attributes:
```js
parse(
'<hr id="foo" class="bar" data-attr="baz" custom="qux" style="top:42px;">'
);
```
### replace
The `replace` option allows you to replace an element with another element.
The `replace` callback's first argument is [domhandler](https://github.com/fb55/domhandler#example)'s node:
```js
parse('<br>', {
replace: domNode => {
console.dir(domNode, { depth: null });
}
});
```
Console output:
```js
Element {
type: 'tag',
parent: null,
prev: null,
next: null,
startIndex: null,
endIndex: null,
children: [],
name: 'br',
attribs: {}
}
```
The element is replaced if a **valid** React element is returned:
```jsx
parse('<p id="replace">text</p>', {
replace: domNode => {
if (domNode.attribs && domNode.attribs.id === 'replace') {
return <span>replaced</span>;
}
}
});
```
For TypeScript projects, you may need to check that `domNode` is an instance of domhandler's `Element`:
```tsx
import { HTMLReactParserOptions } from 'html-react-parser';
import { Element } from 'domhandler/lib/node';
const options: HTMLReactParserOptions = {
replace: domNode => {
if (domNode instanceof Element && domNode.attribs) {
// ...
}
}
};
```
The following [example](https://repl.it/@remarkablemark/html-react-parser-replace-example) modifies the element along with its children:
```jsx
import parse, { domToReact } from 'html-react-parser';
const html = `
<p id="main">
<span class="prettify">
keep me and make me pretty!
</span>
</p>
`;
const options = {
replace: ({ attribs, children }) => {
if (!attribs) {
return;
}
if (attribs.id === 'main') {
return <h1 style={{ fontSize: 42 }}>{domToReact(children, options)}</h1>;
}
if (attribs.class === 'prettify') {
return (
<span style={{ color: 'hotpink' }}>
{domToReact(children, options)}
</span>
);
}
}
};
parse(html, options);
```
HTML output:
<!-- prettier-ignore-start -->
```html
<h1 style="font-size:42px">
<span style="color:hotpink">
keep me and make me pretty!
</span>
</h1>
```
<!-- prettier-ignore-end -->
Convert DOM attributes to React props with `attributesToProps`:
```jsx
import parse, { attributesToProps } from 'html-react-parser';
const html = `
<main class="prettify" style="background: #fff; text-align: center;" />
`;
const options = {
replace: domNode => {
if (domNode.attribs && domNode.name === 'main') {
const props = attributesToProps(domNode.attribs);
return <div {...props} />;
}
}
};
parse(html, options);
```
HTML output:
```html
<div class="prettify" style="background:#fff;text-align:center"></div>
```
[Exclude](https://repl.it/@remarkablemark/html-react-parser-56) an element from rendering by replacing it with `<React.Fragment>`:
```jsx
parse('<p><br id="remove"></p>', {
replace: ({ attribs }) => attribs && attribs.id === 'remove' && <></>
});
```
HTML output:
```html
<p></p>
```
### library
This option specifies the library that creates elements. The default library is **React**.
To use Preact:
```js
parse('<br>', {
library: require('preact')
});
```
Or a custom library:
```js
parse('<br>', {
library: {
cloneElement: () => {
/* ... */
},
createElement: () => {
/* ... */
},
isValidElement: () => {
/* ... */
}
}
});
```
### htmlparser2
Along with the default [htmlparser2 options](https://github.com/fb55/htmlparser2/wiki/Parser-options#option-xmlmode), the parser also sets:
```json
{
"lowerCaseAttributeNames": false
}
```
Since [v0.12.0](https://github.com/remarkablemark/html-react-parser/tree/v0.12.0), the htmlparser2 options can be overridden.
The following example enables [`xmlMode`](https://github.com/fb55/htmlparser2/wiki/Parser-options#option-xmlmode) but disables [`lowerCaseAttributeNames`](https://github.com/fb55/htmlparser2/wiki/Parser-options#option-lowercaseattributenames):
```js
parse('<p /><p />', {
htmlparser2: {
xmlMode: true
}
});
```
> **WARNING**: `htmlparser2` options do not apply on the _client-side_ (browser). The options only apply on the _server-side_ (Node.js). By overriding `htmlparser2` options, universal rendering can break. Do this at your own risk.
### trim
Normally, whitespace is preserved:
```js
parse('<br>\n'); // [React.createElement('br'), '\n']
```
Enable the `trim` option to remove whitespace:
```js
parse('<br>\n', { trim: true }); // React.createElement('br')
```
This fixes the warning:
```
Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of <table>. Make sure you don't have any extra whitespace between tags on each line of your source code.
```
However, intentional whitespace may be stripped out:
```js
parse('<p> </p>', { trim: true }); // React.createElement('p')
```
## Migration
### v1.0.0
TypeScript projects will need to update the types in [v1.0.0](https://github.com/remarkablemark/html-react-parser/releases/tag/v1.0.0).
For the `replace` option, you may need to do the following:
```tsx
import { Element } from 'domhandler/lib/node';
parse('<br class="remove">', {
replace: domNode => {
if (domNode instanceof Element && domNode.attribs.class === 'remove') {
return <></>;
}
}
});
```
Since [v1.1.1](https://github.com/remarkablemark/html-react-parser/releases/tag/v1.1.1), Internet Explorer 9 (IE9) is no longer supported.
## FAQ
### Is this XSS safe?
No, this library is _**not**_ [XSS (cross-site scripting)](https://wikipedia.org/wiki/Cross-site_scripting) safe. See [#94](https://github.com/remarkablemark/html-react-parser/issues/94).
### Does invalid HTML get sanitized?
No, this library does _**not**_ sanitize HTML. See [#124](https://github.com/remarkablemark/html-react-parser/issues/124), [#125](https://github.com/remarkablemark/html-react-parser/issues/125), and [#141](https://github.com/remarkablemark/html-react-parser/issues/141).
### Are `<script>` tags parsed?
Although `<script>` tags and their contents are rendered on the server-side, they're not evaluated on the client-side. See [#98](https://github.com/remarkablemark/html-react-parser/issues/98).
### Attributes aren't getting called
The reason why your HTML attributes aren't getting called is because [inline event handlers](https://developer.mozilla.org/docs/Web/Guide/Events/Event_handlers) (e.g., `onclick`) are parsed as a _string_ rather than a _function_. See [#73](https://github.com/remarkablemark/html-react-parser/issues/73).
### Parser throws an error
If the parser throws an erorr, check if your arguments are valid. See ["Does invalid HTML get sanitized?"](#does-invalid-html-get-sanitized).
### Is SSR supported?
Yes, server-side rendering on Node.js is supported by this library. See [demo](https://repl.it/@remarkablemark/html-react-parser-SSR).
### Elements aren't nested correctly
If your elements are nested incorrectly, check to make sure your [HTML markup is valid](https://validator.w3.org/). The HTML to DOM parsing will be affected if you're using self-closing syntax (`/>`) on non-void elements:
```js
parse('<div /><div />'); // returns single element instead of array of elements
```
See [#158](https://github.com/remarkablemark/html-react-parser/issues/158).
### Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of table
Enable the [trim](#trim) option. See [#155](https://github.com/remarkablemark/html-react-parser/issues/155).
### Don't change case of tags
Tags are lowercased by default. To prevent that from happening, pass the [htmlparser2 option](#htmlparser2):
```js
const options = {
htmlparser2: {
lowerCaseTags: false
}
};
parse('<CustomElement>', options); // React.createElement('CustomElement')
```
> **Warning**: By preserving case-sensitivity of the tags, you may get rendering warnings like:
>
> ```
> Warning: <CustomElement> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
> ```
See [#62](https://github.com/remarkablemark/html-react-parser/issues/62) and [example](https://repl.it/@remarkablemark/html-react-parser-62).
### TS Error: Property 'attribs' does not exist on type 'DOMNode'
The TypeScript error occurs because `DOMNode` needs be an instance of domhandler's `Element`. See [migration](#migration) or [#199](https://github.com/remarkablemark/html-react-parser/issues/199).
### Can I enable `trim` for certain elements?
Yes, you can enable or disable [`trim`](#trim) for certain elements using the [`replace`](#replace) option. See [#205](https://github.com/remarkablemark/html-react-parser/issues/205).
## Performance
Run benchmark:
```sh
$ npm run test:benchmark
```
Output of benchmark run on MacBook Pro 2017:
```
html-to-react - Single x 415,186 ops/sec ±0.92% (85 runs sampled)
html-to-react - Multiple x 139,780 ops/sec ±2.32% (87 runs sampled)
html-to-react - Complex x 8,118 ops/sec ±2.99% (82 runs sampled)
```
Run [Size Limit](https://github.com/ai/size-limit):
```sh
$ npx size-limit
```
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](https://github.com/remarkablemark/html-react-parser/blob/master/CONTRIBUTING.md)].
[](https://github.com/remarkablemark/html-react-parser/graphs/contributors)
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/html-react-parser/contribute)]
#### Individuals
[](https://opencollective.com/html-react-parser)
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/html-react-parser/contribute)]
[](https://opencollective.com/html-react-parser/organization/0/website)
[](https://opencollective.com/html-react-parser/organization/1/website)
[](https://opencollective.com/html-react-parser/organization/2/website)
[](https://opencollective.com/html-react-parser/organization/3/website)
[](https://opencollective.com/html-react-parser/organization/4/website)
[](https://opencollective.com/html-react-parser/organization/5/website)
[](https://opencollective.com/html-react-parser/organization/6/website)
[](https://opencollective.com/html-react-parser/organization/7/website)
[](https://opencollective.com/html-react-parser/organization/8/website)
[](https://opencollective.com/html-react-parser/organization/9/website)
## Support
- [GitHub Sponsors](https://b.remarkabl.org/github-sponsors)
- [Open Collective](https://b.remarkabl.org/open-collective-html-react-parser)
- [Tidelift](https://b.remarkabl.org/tidelift-html-react-parser)
- [Patreon](https://b.remarkabl.org/patreon)
- [Ko-fi](https://b.remarkabl.org/ko-fi)
- [Liberapay](https://b.remarkabl.org/liberapay)
- [Teepsring](https://b.remarkabl.org/teespring)
## License
[MIT](https://github.com/remarkablemark/html-react-parser/blob/master/LICENSE)