UNPKG

16.8 kBMarkdownView Raw
1# html-react-parser
2
3[![NPM](https://nodei.co/npm/html-react-parser.png)](https://nodei.co/npm/html-react-parser/)
4
5[![NPM version](https://img.shields.io/npm/v/html-react-parser.svg)](https://www.npmjs.com/package/html-react-parser)
6[![Build Status](https://github.com/remarkablemark/html-react-parser/workflows/build/badge.svg?branch=master)](https://github.com/remarkablemark/html-react-parser/actions?query=workflow%3Abuild)
7[![Coverage Status](https://coveralls.io/repos/github/remarkablemark/html-react-parser/badge.svg?branch=master)](https://coveralls.io/github/remarkablemark/html-react-parser?branch=master)
8[![Dependency status](https://david-dm.org/remarkablemark/html-react-parser.svg)](https://david-dm.org/remarkablemark/html-react-parser)
9[![NPM downloads](https://img.shields.io/npm/dm/html-react-parser.svg?style=flat-square)](https://www.npmjs.com/package/html-react-parser)
10[![Discord](https://img.shields.io/discord/422421589582282752.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/njExwXdrRJ)
11
12HTML to React parser that works on both the server (Node.js) and the client (browser):
13
14```
15HTMLReactParser(string[, options])
16```
17
18The parser converts an HTML string to one or more [React elements](https://reactjs.org/docs/react-api.html#creating-react-elements).
19
20To replace an element with another element, check out the [`replace`](#replace) option.
21
22#### Example
23
24```js
25const parse = require('html-react-parser');
26parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')
27```
28
29[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)
30
31<details>
32<summary>Table of Contents</summary>
33
34- [Install](#install)
35- [Usage](#usage)
36 - [replace](#replace)
37 - [library](#library)
38 - [htmlparser2](#htmlparser2)
39 - [trim](#trim)
40- [Migration](#migration)
41 - [v1.0.0](#v100)
42- [FAQ](#faq)
43 - [Is this XSS safe?](#is-this-xss-safe)
44 - [Does invalid HTML get sanitized?](#does-invalid-html-get-sanitized)
45 - [Are `<script>` tags parsed?](#are-script-tags-parsed)
46 - [Attributes aren't getting called](#attributes-arent-getting-called)
47 - [Parser throws an error](#parser-throws-an-error)
48 - [Is SSR supported?](#is-ssr-supported)
49 - [Elements aren't nested correctly](#elements-arent-nested-correctly)
50 - [Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of table](#warning-validatedomnesting-whitespace-text-nodes-cannot-appear-as-a-child-of-table)
51 - [Don't change case of tags](#dont-change-case-of-tags)
52 - [TS Error: Property 'attribs' does not exist on type 'DOMNode'](#ts-error-property-attribs-does-not-exist-on-type-domnode)
53 - [Can I enable `trim` for certain elements?](#can-i-enable-trim-for-certain-elements)
54- [Performance](#performance)
55- [Contributors](#contributors)
56 - [Code Contributors](#code-contributors)
57 - [Financial Contributors](#financial-contributors)
58 - [Individuals](#individuals)
59 - [Organizations](#organizations)
60- [Support](#support)
61- [License](#license)
62
63</details>
64
65## Install
66
67[NPM](https://www.npmjs.com/package/html-react-parser):
68
69```sh
70$ npm install html-react-parser --save
71```
72
73[Yarn](https://yarnpkg.com/package/html-react-parser):
74
75```sh
76$ yarn add html-react-parser
77```
78
79[CDN](https://unpkg.com/html-react-parser/):
80
81```html
82<!-- HTMLReactParser depends on React -->
83<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
84<script src="https://unpkg.com/html-react-parser@latest/dist/html-react-parser.min.js"></script>
85<script>
86 window.HTMLReactParser(/* string */);
87</script>
88```
89
90## Usage
91
92Import or require the module:
93
94```js
95// ES Modules
96import parse from 'html-react-parser';
97
98// CommonJS
99const parse = require('html-react-parser');
100```
101
102Parse single element:
103
104```js
105parse('<h1>single</h1>');
106```
107
108Parse multiple elements:
109
110```js
111parse('<li>Item 1</li><li>Item 2</li>');
112```
113
114Make sure to render parsed adjacent elements under a parent element:
115
116```jsx
117<ul>
118 {parse(`
119 <li>Item 1</li>
120 <li>Item 2</li>
121 `)}
122</ul>
123```
124
125Parse nested elements:
126
127```js
128parse('<body><p>Lorem ipsum</p></body>');
129```
130
131Parse element with attributes:
132
133```js
134parse(
135 '<hr id="foo" class="bar" data-attr="baz" custom="qux" style="top:42px;">'
136);
137```
138
139### replace
140
141The `replace` option allows you to replace an element with another element.
142
143The `replace` callback's first argument is [domhandler](https://github.com/fb55/domhandler#example)'s node:
144
145```js
146parse('<br>', {
147 replace: domNode => {
148 console.dir(domNode, { depth: null });
149 }
150});
151```
152
153Console output:
154
155```js
156Element {
157 type: 'tag',
158 parent: null,
159 prev: null,
160 next: null,
161 startIndex: null,
162 endIndex: null,
163 children: [],
164 name: 'br',
165 attribs: {}
166}
167```
168
169The element is replaced if a **valid** React element is returned:
170
171```jsx
172parse('<p id="replace">text</p>', {
173 replace: domNode => {
174 if (domNode.attribs && domNode.attribs.id === 'replace') {
175 return <span>replaced</span>;
176 }
177 }
178});
179```
180
181For TypeScript projects, you may need to check that `domNode` is an instance of domhandler's `Element`:
182
183```tsx
184import { HTMLReactParserOptions } from 'html-react-parser';
185import { Element } from 'domhandler/lib/node';
186
187const options: HTMLReactParserOptions = {
188 replace: domNode => {
189 if (domNode instanceof Element && domNode.attribs) {
190 // ...
191 }
192 }
193};
194```
195
196The following [example](https://repl.it/@remarkablemark/html-react-parser-replace-example) modifies the element along with its children:
197
198```jsx
199import parse, { domToReact } from 'html-react-parser';
200
201const html = `
202 <p id="main">
203 <span class="prettify">
204 keep me and make me pretty!
205 </span>
206 </p>
207`;
208
209const options = {
210 replace: ({ attribs, children }) => {
211 if (!attribs) {
212 return;
213 }
214
215 if (attribs.id === 'main') {
216 return <h1 style={{ fontSize: 42 }}>{domToReact(children, options)}</h1>;
217 }
218
219 if (attribs.class === 'prettify') {
220 return (
221 <span style={{ color: 'hotpink' }}>
222 {domToReact(children, options)}
223 </span>
224 );
225 }
226 }
227};
228
229parse(html, options);
230```
231
232HTML output:
233
234<!-- prettier-ignore-start -->
235
236```html
237<h1 style="font-size:42px">
238 <span style="color:hotpink">
239 keep me and make me pretty!
240 </span>
241</h1>
242```
243
244<!-- prettier-ignore-end -->
245
246Convert DOM attributes to React props with `attributesToProps`:
247
248```jsx
249import parse, { attributesToProps } from 'html-react-parser';
250
251const html = `
252 <main class="prettify" style="background: #fff; text-align: center;" />
253`;
254
255const options = {
256 replace: domNode => {
257 if (domNode.attribs && domNode.name === 'main') {
258 const props = attributesToProps(domNode.attribs);
259 return <div {...props} />;
260 }
261 }
262};
263
264parse(html, options);
265```
266
267HTML output:
268
269```html
270<div class="prettify" style="background:#fff;text-align:center"></div>
271```
272
273[Exclude](https://repl.it/@remarkablemark/html-react-parser-56) an element from rendering by replacing it with `<React.Fragment>`:
274
275```jsx
276parse('<p><br id="remove"></p>', {
277 replace: ({ attribs }) => attribs && attribs.id === 'remove' && <></>
278});
279```
280
281HTML output:
282
283```html
284<p></p>
285```
286
287### library
288
289This option specifies the library that creates elements. The default library is **React**.
290
291To use Preact:
292
293```js
294parse('<br>', {
295 library: require('preact')
296});
297```
298
299Or a custom library:
300
301```js
302parse('<br>', {
303 library: {
304 cloneElement: () => {
305 /* ... */
306 },
307 createElement: () => {
308 /* ... */
309 },
310 isValidElement: () => {
311 /* ... */
312 }
313 }
314});
315```
316
317### htmlparser2
318
319Along with the default [htmlparser2 options](https://github.com/fb55/htmlparser2/wiki/Parser-options#option-xmlmode), the parser also sets:
320
321```json
322{
323 "lowerCaseAttributeNames": false
324}
325```
326
327Since [v0.12.0](https://github.com/remarkablemark/html-react-parser/tree/v0.12.0), the htmlparser2 options can be overridden.
328
329The 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):
330
331```js
332parse('<p /><p />', {
333 htmlparser2: {
334 xmlMode: true
335 }
336});
337```
338
339> **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.
340
341### trim
342
343Normally, whitespace is preserved:
344
345```js
346parse('<br>\n'); // [React.createElement('br'), '\n']
347```
348
349Enable the `trim` option to remove whitespace:
350
351```js
352parse('<br>\n', { trim: true }); // React.createElement('br')
353```
354
355This fixes the warning:
356
357```
358Warning: 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.
359```
360
361However, intentional whitespace may be stripped out:
362
363```js
364parse('<p> </p>', { trim: true }); // React.createElement('p')
365```
366
367## Migration
368
369### v1.0.0
370
371TypeScript projects will need to update the types in [v1.0.0](https://github.com/remarkablemark/html-react-parser/releases/tag/v1.0.0).
372
373For the `replace` option, you may need to do the following:
374
375```tsx
376import { Element } from 'domhandler/lib/node';
377
378parse('<br class="remove">', {
379 replace: domNode => {
380 if (domNode instanceof Element && domNode.attribs.class === 'remove') {
381 return <></>;
382 }
383 }
384});
385```
386
387Since [v1.1.1](https://github.com/remarkablemark/html-react-parser/releases/tag/v1.1.1), Internet Explorer 9 (IE9) is no longer supported.
388
389## FAQ
390
391### Is this XSS safe?
392
393No, 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).
394
395### Does invalid HTML get sanitized?
396
397No, 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).
398
399### Are `<script>` tags parsed?
400
401Although `<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).
402
403### Attributes aren't getting called
404
405The 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).
406
407### Parser throws an error
408
409If the parser throws an erorr, check if your arguments are valid. See ["Does invalid HTML get sanitized?"](#does-invalid-html-get-sanitized).
410
411### Is SSR supported?
412
413Yes, server-side rendering on Node.js is supported by this library. See [demo](https://repl.it/@remarkablemark/html-react-parser-SSR).
414
415### Elements aren't nested correctly
416
417If 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:
418
419```js
420parse('<div /><div />'); // returns single element instead of array of elements
421```
422
423See [#158](https://github.com/remarkablemark/html-react-parser/issues/158).
424
425### Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of table
426
427Enable the [trim](#trim) option. See [#155](https://github.com/remarkablemark/html-react-parser/issues/155).
428
429### Don't change case of tags
430
431Tags are lowercased by default. To prevent that from happening, pass the [htmlparser2 option](#htmlparser2):
432
433```js
434const options = {
435 htmlparser2: {
436 lowerCaseTags: false
437 }
438};
439parse('<CustomElement>', options); // React.createElement('CustomElement')
440```
441
442> **Warning**: By preserving case-sensitivity of the tags, you may get rendering warnings like:
443>
444> ```
445> Warning: <CustomElement> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
446> ```
447
448See [#62](https://github.com/remarkablemark/html-react-parser/issues/62) and [example](https://repl.it/@remarkablemark/html-react-parser-62).
449
450### TS Error: Property 'attribs' does not exist on type 'DOMNode'
451
452The 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).
453
454### Can I enable `trim` for certain elements?
455
456Yes, 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).
457
458## Performance
459
460Run benchmark:
461
462```sh
463$ npm run test:benchmark
464```
465
466Output of benchmark run on MacBook Pro 2017:
467
468```
469html-to-react - Single x 415,186 ops/sec ±0.92% (85 runs sampled)
470html-to-react - Multiple x 139,780 ops/sec ±2.32% (87 runs sampled)
471html-to-react - Complex x 8,118 ops/sec ±2.99% (82 runs sampled)
472```
473
474Run [Size Limit](https://github.com/ai/size-limit):
475
476```sh
477$ npx size-limit
478```
479
480## Contributors
481
482### Code Contributors
483
484This project exists thanks to all the people who contribute. [[Contribute](https://github.com/remarkablemark/html-react-parser/blob/master/CONTRIBUTING.md)].
485
486[![Code Contributors](https://opencollective.com/html-react-parser/contributors.svg?width=890&button=false)](https://github.com/remarkablemark/html-react-parser/graphs/contributors)
487
488### Financial Contributors
489
490Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/html-react-parser/contribute)]
491
492#### Individuals
493
494[![Financial Contributors - Individuals](https://opencollective.com/html-react-parser/individuals.svg?width=890)](https://opencollective.com/html-react-parser)
495
496#### Organizations
497
498Support 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)]
499
500[![Financial Contributors - Organization 0](https://opencollective.com/html-react-parser/organization/0/avatar.svg)](https://opencollective.com/html-react-parser/organization/0/website)
501[![Financial Contributors - Organization 1](https://opencollective.com/html-react-parser/organization/1/avatar.svg)](https://opencollective.com/html-react-parser/organization/1/website)
502[![Financial Contributors - Organization 2](https://opencollective.com/html-react-parser/organization/2/avatar.svg)](https://opencollective.com/html-react-parser/organization/2/website)
503[![Financial Contributors - Organization 3](https://opencollective.com/html-react-parser/organization/3/avatar.svg)](https://opencollective.com/html-react-parser/organization/3/website)
504[![Financial Contributors - Organization 4](https://opencollective.com/html-react-parser/organization/4/avatar.svg)](https://opencollective.com/html-react-parser/organization/4/website)
505[![Financial Contributors - Organization 5](https://opencollective.com/html-react-parser/organization/5/avatar.svg)](https://opencollective.com/html-react-parser/organization/5/website)
506[![Financial Contributors - Organization 6](https://opencollective.com/html-react-parser/organization/6/avatar.svg)](https://opencollective.com/html-react-parser/organization/6/website)
507[![Financial Contributors - Organization 7](https://opencollective.com/html-react-parser/organization/7/avatar.svg)](https://opencollective.com/html-react-parser/organization/7/website)
508[![Financial Contributors - Organization 8](https://opencollective.com/html-react-parser/organization/8/avatar.svg)](https://opencollective.com/html-react-parser/organization/8/website)
509[![Financial Contributors - Organization 9](https://opencollective.com/html-react-parser/organization/9/avatar.svg)](https://opencollective.com/html-react-parser/organization/9/website)
510
511## Support
512
513- [GitHub Sponsors](https://b.remarkabl.org/github-sponsors)
514- [Open Collective](https://b.remarkabl.org/open-collective-html-react-parser)
515- [Tidelift](https://b.remarkabl.org/tidelift-html-react-parser)
516- [Patreon](https://b.remarkabl.org/patreon)
517- [Ko-fi](https://b.remarkabl.org/ko-fi)
518- [Liberapay](https://b.remarkabl.org/liberapay)
519- [Teepsring](https://b.remarkabl.org/teespring)
520
521## License
522
523[MIT](https://github.com/remarkablemark/html-react-parser/blob/master/LICENSE)