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