Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/aknuds1/html-to-react

A lightweight library that converts raw HTML to a React DOM structure.
https://github.com/aknuds1/html-to-react

conversion html javascript javascript-library npm-package react

Last synced: 3 months ago
JSON representation

A lightweight library that converts raw HTML to a React DOM structure.

Lists

README

        

# html-to-react

[![Greenkeeper badge](https://badges.greenkeeper.io/aknuds1/html-to-react.svg)](https://greenkeeper.io/)
[![Build Status](https://travis-ci.org/aknuds1/html-to-react.svg?branch=master)](https://travis-ci.org/aknuds1/html-to-react)
[![npm version](https://badge.fury.io/js/html-to-react.svg)](http://badge.fury.io/js/html-to-react)
[![Dependency Status](https://david-dm.org/aknuds1/html-to-react.svg)](https://david-dm.org/aknuds1/html-to-react)
[![Coverage Status](https://coveralls.io/repos/aknuds1/html-to-react/badge.svg?branch=master)](https://coveralls.io/r/aknuds1/html-to-react?branch=master)
[![npm](https://img.shields.io/npm/dm/html-to-react.svg?maxAge=2592000)](https://www.npmjs.com/package/html-to-react)

A lightweight library that converts raw HTML to a React DOM structure.

## Why?
I had a scenario where an HTML template was generated by a different team, yet I wanted to leverage
React for the parts I did have control over. The template basically contains something like:

```html













```

I had to replace each `

` that contains a `data-report-id` attribute with an actual report,
which was nothing more than a React component.

Simply replacing the `

` elements with a React component would end up with multiple top-level
React components that have no common parent.

The **html-to-react** module solves this problem by parsing each DOM element and converting it to a
React tree with one single parent.

## Installation

`$ npm install --save html-to-react`

## Examples

### Simple

The following example parses each node and its attributes and returns a tree of React elements.

```javascript
const ReactDOMServer = require('react-dom/server');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlInput = '


Title


A paragraph


';
const htmlToReactParser = new HtmlToReactParser();
const reactElement = htmlToReactParser.parse(htmlInput);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);

assert.equal(reactHtml, htmlInput); // true
```

### With Custom Processing Instructions

If certain DOM nodes require specific processing, for example if you want to capitalize each
`

` tag, the following example demonstrates this:

```javascript
const ReactDOMServer = require('react-dom/server');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlInput = '


Title


Paragraph


Another title


';
const htmlExpected = '

TITLE


Paragraph


ANOTHER TITLE


';

const isValidNode = function () {
return true;
};

// Order matters. Instructions are processed in the order they're defined
const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();
const processingInstructions = [
{
// Custom

processing
shouldProcessNode: function (node) {
return node.parent && node.parent.name && node.parent.name === 'h1';
},
processNode: function (node, children) {
return node.data.toUpperCase();
}
},
{
// Anything else
shouldProcessNode: function (node) {
return true;
},
processNode: processNodeDefinitions.processDefaultNode
}
];
const htmlToReactParser = new HtmlToReactParser();
const reactComponent = htmlToReactParser.parseWithInstructions(htmlInput, isValidNode,
processingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactComponent);
assert.equal(reactHtml, htmlExpected);
```

### Replace the Children of an Element

There may be a situation where you want to replace the children of an element with a React
component. This is beneficial if you want to:
- a) Preserve the containing element
- b) Not rely on any child node to insert your React component

#### Example

Below is a simple template that could get loaded via ajax into your application

##### Before
```html




Sample Heading


Sample Text





```

##### After

You may want to extract the inner html from the `data-container` attribute, store it and then pass
it as a prop to your injected `RichTextEditor`.

```html




Sample heading

Sample Text

"} />



```

#### Setup

In your instructions object, you must specify `replaceChildren: true`.

```javascript
const React = require('react');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlToReactParser = new HtmlToReactParser();
const htmlInput = '


Text


Text


';
const htmlExpected = '

Heading

';

const isValidNode = function () {
return true;
};

const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();

// Order matters. Instructions are processed in
// the order they're defined
const processingInstructions = [
{
// This is REQUIRED, it tells the parser
// that we want to insert our React
// component as a child
replaceChildren: true,
shouldProcessNode: function (node) {
return node.attribs && node.attribs['data-test'] === 'foo';
},
processNode: function (node, children, index) {
return React.createElement('h1', {key: index,}, 'Heading');
}
},
{
// Anything else
shouldProcessNode: function (node) {
return true;
},
processNode: processNodeDefinitions.processDefaultNode,
},
];

const reactComponent = htmlToReactParser.parseWithInstructions(
htmlInput, isValidNode, processingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(
reactComponent);
assert.equal(reactHtml, htmlExpected);
```

### With Preprocessing Instructions

There may be situations where you want to preprocess nodes before rendering them, analogously
to the custom processing instructions functionality. The rationale for supporting preprocessing
hooks is generally that you might want to apply more general processing to nodes, before
applying custom processing hooks to filtered sets of nodes. You could accomplish the same by
calling common functions from your custom processing hooks, but the preprocessing hooks
API makes it more convenient.

#### Example

Below is a simple template in which you may want to replace div IDs, via a preprocessing hook:

```html



Sample For First




Sample For Second




```

We want to process the above HTML into the following:

```html


First


Second



```

We can accomplish that with the following script, using a combination of preprocessing and
custom processing instructions:

```javascript
const React = require('react');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlToReactParser = new HtmlToReactParser();
const htmlInput = '

' +
'
' +
'

Sample For First

' +
'
' +
'
' +
'

Sample For Second

' +
'
' +
'
';

const htmlExpected = '

' +
'

First

' +
'

Second

' +
'
';

const isValidNode = function () {
return true;
};

const preprocessingInstructions = [
{
shouldPreprocessNode: function (node) {
return node.attribs && node.attribs['data-process'] === 'shared';
},
preprocessNode: function (node) {
node.attribs = {id: `preprocessed-${node.attribs.id}`,};
},
}
];
const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();
const processingInstructions = [
{
shouldProcessNode: function (node) {
return node.attribs && node.attribs.id === 'preprocessed-first';
},
processNode: function(node, children, index) {
return React.createElement('h1', {key: index, id: node.attribs.id,}, 'First');
},
},
{
shouldProcessNode: function (node) {
return node.attribs && node.attribs.id === 'preprocessed-second';
},
processNode: function (node, children, index) {
return React.createElement('h2', {key: index, id: node.attribs.id,}, 'Second');
},
},
{
shouldProcessNode: function (node) {
return true;
},
processNode: processNodeDefinitions.processDefaultNode,
},
];

const reactComponent = parser.parseWithInstructions(htmlInput, isValidNode, processingInstructions,
preprocessingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactComponent);
assert.equal(reactHtml, htmlExpected);
```

## Tests & Coverage

Test locally: `$ npm test`

Test with coverage and report coverage to Coveralls: `$ npm run test-coverage`

Test with coverage and open HTML report: `$ npm run test-html-coverage`