Sing
Sing

Reputation: 4052

How to parse html to React component?

This is my senario :
1. Application request CMS(Content management system) for page contents.
2. CMS return "<div>Hi,<SpecialButton color="red">My Button</SpecialButton></div>"
3. Application consume the content, render corresponding component with data provided in attribute.

I can't figure out how to do step 3 in React way, any advice is appreciated.

Thanks @Glenn Reyes, here's a Sandbox to show the problem.

import React from 'react';
import { render } from 'react-dom';

const SpecialButton = ({ children, color }) => (
  <button style={{color}}>{children}</button>
);

const htmlFromCMS = `
<div>Hi, 
  <SpecialButton color="red">My Button</SpecialButton>
</div>`;

const App = () => (
  <div dangerouslySetInnerHTML={{__html: htmlFromCMS}}>
  </div>
);

// expect to be same as
// const App = () => (
//   <div>Hi, 
//     <SpecialButton color="red">My Button</SpecialButton>
//   </div>
// );

render(<App />, document.getElementById('root'));

Here is a live demo made by Vuejs. String "<div v-demo-widget></div>" could be treat as Vuejs directive and rendered. Source Code.

Upvotes: 51

Views: 142933

Answers (7)

Ramesh HB
Ramesh HB

Reputation: 7

Simple and easiest way to achieve parser by using dangerouslySetInnerHTML attribute.

const htmlString = '<h1>Hello World! 👋</h1>';
const App = () => (
  <div dangerouslySetInnerHTML={{ __html: htmlString }} />
);

Upvotes: 0

Mike Chen
Mike Chen

Reputation: 1

This is my way to use html-react-parser and react onClick event together.

import React from "react";
import { render } from "react-dom";
import parse from "html-react-parser";

const html = `
  <div style="font-size:32px;">html-react-parser with js events</div>
  <div>This is a long long long text.<div id="supportEmail"></div>t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</div>
`;

const handlefunction = () => {
  alert("Clicked");
};

const replace = (domNode) => {
  if (domNode.attribs && domNode.attribs.id === "supportEmail") {
    return (
      <code>
        <div
          style={{
            backgroundColor: "gray",
            padding: "4px 8px",
            width: "100px",
            textAlign: "center"
          }}
          onClick={handlefunction}
        >
          Click
        </div>
      </code>
    );
  }
};

function App() {
  return parse(html, { replace });
}

render(<App />, document.getElementById("root"));

Check example in Codesandbox

Upvotes: 0

Hans
Hans

Reputation: 1564

As pointed out in this answer by EsterlingAccimeYoutuber, you can use a parser in case you don't want to use dangerouslySetInnerHTML attribute.

By now, react-html-parser has not been updated for 3 years, so I went looking for a different module.

html-react-parser does same job but is frequently maintained and updated.

It should be good practice to sanitize your html-String to prevent XSS attacks. dompurify can be used for that.

I updated EsterlingAccimeYoutuber's code-example to the following:

import React from 'react';
import { render } from 'react-dom';
import parse from 'html-react-parser';
import DOMPurify from 'dompurify';

const SpecialButton = ({ children, color }) => (
  <button style={{color}}>{children}</button>
);

const htmlFromCMS = `
<div>Hi, 
  <SpecialButton color="red">My Button</SpecialButton>
</div>`;

const htmlFrom = (htmlString) => {
        const cleanHtmlString = DOMPurify.sanitize(htmlString,
          { USE_PROFILES: { html: true } });
        const html = parse(cleanHtmlString);
        return html;
}

const App = () => (
  <div>
     {htmlFromCMS && htmlFrom(htmlFromCMS)}
  </div>
);


render(<App />, document.getElementById('root'));

Inspired by original post above, hence special thanks to original authors!

Upvotes: 28

accimeesterlin
accimeesterlin

Reputation: 4718

You can use the react-html-parser in case you don't want to use dangerouslySetInnerHTML attribute

import React from 'react';
import { render } from 'react-dom';
import ReactHtmlParser from 'react-html-parser';

const SpecialButton = ({ children, color }) => (
  <button style={{color}}>{children}</button>
);

const htmlFromCMS = `
<div>Hi, 
  <SpecialButton color="red">My Button</SpecialButton>
</div>`;

const App = () => (
  <div>
     {ReactHtmlParser(htmlFromCMS)}
  </div>
);


render(<App />, document.getElementById('root'));

Happy Coding!!!

Upvotes: 24

lost_in_magento
lost_in_magento

Reputation: 683

For any from the future just enhancement of GProst Answer, You can use ReactDOMserver, This is how we can implement the same.

import React from "react";
import { render } from "react-dom";
import { renderToString } from "react-dom/server";

const SpecialButton = ({ children, color }) => (
  <button style={{ color }}>{children}</button>
);

const renderButton = renderToString(<SpecialButton>MyButton</SpecialButton>);

const htmlFromCMS = `
<div>Hi, 
  ${renderButton}
</div>`;

const App = () => <div dangerouslySetInnerHTML={{ __html: htmlFromCMS }} />;

render(<App />, document.getElementById("root"));

Upvotes: 1

glennreyes
glennreyes

Reputation: 2349

You probably want to look deeper into dangerouslySetInnerHTML. Here is an example how to render HTML from a string in a React component:

import React from 'react';
import { render } from 'react-dom';

const htmlString = '<h1>Hello World! 👋</h1>';

const App = () => (
  <div dangerouslySetInnerHTML={{ __html: htmlString }} />
);

render(<App />, document.getElementById('root'));

Full example here: https://codesandbox.io/s/xv40xXQzE

Read more about dangerouslySetInnerHTML in the React docs here: https://facebook.github.io/react/docs/dom-elements.html#dangerouslysetinnerhtml

Upvotes: 57

GProst
GProst

Reputation: 10237

You can try use ReactDOMserver to render <MyReactComponent /> into html on your server and then pass it to the client, where you can insert all received html via dangerouslySetInnerHTML.

Upvotes: 0

Related Questions