389

Here's what I tried and how it goes wrong.

This works:

<div dangerouslySetInnerHTML={{ __html: "<h1>Hi there!</h1>" }} />

This doesn't:

<div dangerouslySetInnerHTML={{ __html: this.props.match.description }} />

The description property is just a normal string of HTML content. However it's rendered as a string, not as HTML for some reason.

enter image description here

Any suggestions?

0

16 Answers 16

289

Is this.props.match.description a string or an object? If it's a string, it should be converted to HTML just fine. Example:

class App extends React.Component {

constructor() {
    super();
    this.state = {
      description: '<h1 style="color:red;">something</h1>'
    }
  }
  
  render() {
    return (
      <div dangerouslySetInnerHTML={{ __html: this.state.description }} />
    );
  }
}

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

Result: http://codepen.io/ilanus/pen/QKgoLA?editors=1011

However if description is <h1 style="color:red;">something</h1> without the quotes '', you're going to get:

​Object {
$$typeof: [object Symbol] {},
  _owner: null,
  key: null,
  props: Object {
    children: "something",
    style: "color:red;"
  },
  ref: null,
  type: "h1"
}

If It's a string and you don't see any HTML markup the only problem I see is wrong markup..

UPDATE

If you are dealing with HTML Entities, You need to decode them before sending them to dangerouslySetInnerHTML that's why it's called "dangerously" :)

Working example:

class App extends React.Component {

  constructor() {
    super();
    this.state = {
      description: '&lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;'
    }
  }

   htmlDecode(input){
    var e = document.createElement('div');
    e.innerHTML = input;
    return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
  }

  render() {
    return (
      <div dangerouslySetInnerHTML={{ __html: this.htmlDecode(this.state.description) }} />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
Sign up to request clarification or add additional context in comments.

6 Comments

this.props.match.description is a string, not an object. What do you mean with wrong markup? Do you mean unclosed tags? React should just render it no?
Could you paste here console.log(this.props.match.description);
One example: &lt;p&gt;&lt;strong&gt;Our Opportunity:&lt;/strong&gt;&lt;/p&gt;
In this case you need either to use .innerHTML or decode HTMLEntities.
Return multiple lines or HTML code with tags: function htmlDecode(input){ var e = document.createElement('div'); e.innerHTML = input; var returnString = ''; for (index = 0; index < e.childNodes.length; index++) { // case of just a string if(e.childNodes[index].nodeValue){ returnString += e.childNodes[index].nodeValue; } // case of HTML if(e.childNodes[index].outerHTML){ returnString += e.childNodes[index].outerHTML; } } return returnString; }
|
159

I use 'react-html-parser'

yarn add react-html-parser
import ReactHtmlParser from 'react-html-parser'; 

<div> { ReactHtmlParser (html_string) } </div>

Source on npmjs.com

Lifting up @okram's comment for more visibility:

from its github description: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTML from npmjs.com A utility for converting HTML strings into React components. Avoids the use of dangerouslySetInnerHTML and converts standard HTML elements, attributes and inline styles into their React equivalents.

5 Comments

Does this library use "dangerouslySetInnerHTML" in the background?
from its github description: Converts HTML strings directly into React components avoiding the need to use dangerouslySetInnerHTML from npmjs.com A utility for converting HTML strings into React components. Avoids the use of dangerouslySetInnerHTML and converts standard HTML elements, attributes and inline styles into their React equivalents.
For React 17.0+, I use a similar library called "html-react-parser", which is currently supported. npmjs.com/package/html-react-parser
From FAQ section of html-react-parser: "this library is not XSS (cross-site scripting) safe." npmjs.com/package/html-react-parser
You may need to install with : npm install react-html-parser
79

Check if the text you're trying to append to the node is not escaped like this:

var prop = {
    match: {
        description: '&lt;h1&gt;Hi there!&lt;/h1&gt;'
    }
};

Instead of this:

var prop = {
    match: {
        description: '<h1>Hi there!</h1>'
    }
};

if is escaped you should convert it from your server-side.

The node is text because is escaped

The node is text because is escaped

The node is a dom node because isn't escaped

The node is a dom node because isn't escaped

5 Comments

This was the issue. The description string was escaped HTML. I unescaped it and now it works fine.
Please avoid using dangerouslySetInnerHTML instead use Fragment from react v16. Check the next answer by @brad-adams
Appreciate the mention @KunalParekh, but they're different things. My answer is only valid if the html is located within your app (meaning it's actually JSX). To parse HTML from an external source to jsx you'd need to seek another solution.
Convert it how? With which API or package? The answer gets you halfway there.
@TsarBomba there are lots of ways to do it: npm.im/html-entities
71

If you have HTML in a string I would recommend using a package called html-react-parser.

Installation

NPM:

npm install html-react-parser

yarn:

yarn add html-react-parser

Usage

import parse from 'html-react-parser'
const yourHtmlString = '<h1>Hello</h1>'

code:

<div>
    {parse(yourHtmlString)}
</div>

2 Comments

Can confirm it works great!
heads up that this package is a security risk because it doesn't sanitize the html. only use it with a package like sanitize-html: github.com/apostrophecms/sanitize-html
27

I use innerHTML together a ref to span:

import React, { useRef, useEffect, useState } from 'react';

export default function Sample() {
  const spanRef = useRef<HTMLSpanElement>(null);
  const [someHTML,] = useState("some <b>bold</b>");

  useEffect(() => {
    if (spanRef.current) {
      spanRef.current.innerHTML = someHTML;
    }
  }, [spanRef.current, someHTML]);

  return <div>
    my custom text follows<br />
    <span ref={spanRef} />
  </div>
}

note on XSS attack: to prevent malicious injected script to be executed the html code need to be sanitized. See follow example:

/**
 * example how to retrieve a reference to an html object
 */

import React, { useRef, useEffect } from 'react';
import * as DOMPurify from 'dompurify'; // add with yarn add DOMPurify and @types/DOMPurify

/**
 * this component can be used into another for example <Sample/>
 */
export default function Sample() {
    /**
     * 1) spanRef is now a React.RefObject<HTMLSpanElement>
     * initially created with null value
     */
    const spanRef = useRef<HTMLSpanElement>(null);

    /**
     * 2) later, when spanRef changes because html span element with ref attribute,
     * follow useEffect hook will triggered because of dependent [spanRef].
     * in an if ( spanRef.current ) that states if spanRef assigned to valid html obj
     * we do what we need : in this case through current.innerHTML
     */
    useEffect(() => {
        if (spanRef.current) {
            // follow malicious code injects
            // spanRef.current.innerHTML = "sample xss<img src onerror=\"alert('code exec')\"/>";

            // follow malicious code will sanitized to <img src>
            spanRef.current.innerHTML = DOMPurify.sanitize("sample xss prevented<img src onerror=\"alert('code exec')\"/>")
        }
    }, [spanRef]);

    return <div>
        my custom text follows<br />
        {/* ref={spanRef] will update the React.RefObject `spanRef` when html obj ready */}
        <span ref={spanRef} />
    </div>
}

6 Comments

I like this, no need for additional libraries or reliance on server-side when you don't have that luxury. Inspired by you, but in a class component I did componentDidMount() { this.message.current.innerHTML = this.state.selectedMessage.body; } body is the escaped html for me.
A little explanation to the answer could have done wonders.
@letsbondiway see UPDATE part on my answer.
@LorenzoDelana Thank you for the detailed updated answer. Now it is really helpful. However, I had a question - do you think there is any kind of security risks associated with this solution? I mean attacks like XSS, HTML injection and all. My understanding is it is safe from these as we are not using dangerouslySetInnerHTML
@letsbondiway with or without setting an html element property in a direct way such the innerHTML can be dangerous if security criteria aren't applied ; from my point of view, but of course I could miss something, there isn't specific issues if you know what you are doing and how these could negatively used from an attackers. For counter example you can regularly use standard input box provided by the framework that of course is good because best-practices are already taken in account, but if you use that text as a part of raw sql query an attacker could inject a tautology to extract all data.
|
23

If you have control over where the string containing html is coming from (ie. somewhere in your app), you can benefit from the new <Fragment> API, doing something like:

import React, {Fragment} from 'react'

const stringsSomeWithHtml = {
  testOne: (
    <Fragment>
      Some text <strong>wrapped with strong</strong>
    </Fragment>
  ),
  testTwo: `This is just a plain string, but it'll print fine too`,
}

...

render() {
  return <div>{stringsSomeWithHtml[prop.key]}</div>
}

10 Comments

There is no string containing html in your example. It's either jsx or plain string.
Well, yeah technically you're correct @mrkvon, however as I mention, this solution is only valid if said "html"/jsx is something you have control over. Not for rendering some raw html provided via an API, for example. Prior to the Fragment API it was always a pain for me, that required additional span wraps that would sometimes mess with flex layouts. When I stumbled upon this question looking for a possible solution I thought I'd share how I got around things.
Thanks! This was the only solution that worked in my case. Also, responding to mrkvon's comment on this answer : This answer indeed contains html i.e Some text <strong>wrapped with strong</strong> contains html tag strong.
@BinitaBharati But that's not a string though. If you get a string from an API like "<p>This is a String</p>"(or simply store a string in a variable), when you put this string in <Fragment>, the output will still contain the <p> tag.
@BradAdams. Nice trick though. I can see the instances where it becomes handy.
|
19

You just use dangerouslySetInnerHTML method of React

<div dangerouslySetInnerHTML={{ __html: htmlString }} />

Or you can implement more with this easy way: Render the HTML raw in React app

Comments

5

In my case, I used react-render-html

First install the package by npm i --save react-render-html

then,

import renderHTML from 'react-render-html';

renderHTML("<a class='github' href='https://github.com'><b>GitHub</b></a>")

1 Comment

Meanwhile, I do not suggest using react-render-html anymore. Its package health score is very low due to snyk: snyk.io/advisor/npm-package/react-render-html Html-react-parser is much better: snyk.io/advisor/npm-package/html-react-parser
3

Use the react built-in dangerouslySetInnerHTML with a good HTML sanitizer like sanitize-html.

<div dangerouslySetInnerHTML={{
  __html: sanitizeHtml(dirtyHTMLString)
}}></div>

All the HTML parsers I tried would strip some HTML that I didn't intend, and it's not worth the time to dig through the documentation to figure out how to stop that.

By using dangerouslySetInnerHTML like this, you're sanitizing the HTML so it isn't as dangerous.

1 Comment

Its working but one issue its not rendering with space
2

I could not get npm build to work with react-html-parser. However, in my case, I was able to successfully make use of https://reactjs.org/docs/fragments.html. I had a requirement to show few html unicode characters , but they should not be directly embedded in the JSX. Within the JSX, it had to be picked from the Component's state. Component code snippet is given below :

constructor() 
{
this.state = {
      rankMap : {"5" : <Fragment>&#9733; &#9733; &#9733; &#9733; &#9733;</Fragment> , 
                 "4" : <Fragment>&#9733; &#9733; &#9733; &#9733; &#9734;</Fragment>, 
                 "3" : <Fragment>&#9733; &#9733; &#9733; &#9734; &#9734;</Fragment> , 
                 "2" : <Fragment>&#9733; &#9733; &#9734; &#9734; &#9734;</Fragment>, 
                 "1" : <Fragment>&#9733; &#9734; &#9734; &#9734; &#9734;</Fragment>}
                };
}

render() 
{
       return (<div class="card-footer">
                    <small class="text-muted">{ this.state.rankMap["5"] }</small>
               </div>);
}

Comments

1

We could simply use the built-in JavaScript DOM parser:

console.log(
  new DOMParser().parseFromString("<div>Hi!</div>", "text/html").body.innerText
)

Comments

0

i use https://www.npmjs.com/package/html-to-react

const HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = html.template;
let htmlToReactParser = new HtmlToReactParser();
let reactElement = htmlToReactParser.parse(htmlInput); 
return(<div>{reactElement}</div>)

Comments

0

You can also use parseReactHTMLComponent from Jumper Package. Just look at it, it's easy and you don't need to use JSX syntax.

https://codesandbox.io/s/jumper-module-react-simple-parser-3b8c9?file=/src/App.js .

More on Jumper:

https://github.com/Grano22/jumper/blob/master/components.js

NPM Package:

https://www.npmjs.com/package/jumper_react

Comments

0

  // For typescript

import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";

export function contentHandler(postContent: string) {
  const options: HTMLReactParserOptions = {
    replace: (domNode: Element) => {
      if (domNode.attribs) {
        if (domNode.attribs.id === 'shortcode') {
          return <div className="leadform">Shortcode</div>;
        }
      }
    },
  };

  return parse(postContent, options);
}

// Usage: contentHandler("<span>Hello World!</span>")

Comments

0

Got this solution from some other answer on Stackoverflow (can't remember which one):

function getStringWithElements()
{
    let str=[];
    str.push(<div>This <b>truly</b> works. Thanks to {name}!</div>);
    return str;
};

Now inside React render:

return {getStringWithElements()}

Comments

-5

If you have control to the {this.props.match.description} and if you are using JSX. I would recommend not to use "dangerouslySetInnerHTML".

// In JSX, you can define a html object rather than a string to contain raw HTML
let description = <h1>Hi there!</h1>;

// Here is how you print
return (
    {description}
);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.