353

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?

Flip
  • 6,233
  • 7
  • 46
  • 75
Sergio Tapia
  • 9,173
  • 12
  • 35
  • 59

14 Answers14

265

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'));
Dexter
  • 4,036
  • 3
  • 47
  • 55
Ilanus
  • 6,690
  • 5
  • 13
  • 37
  • `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? – Sergio Tapia Sep 28 '16 at 22:31
  • Could you paste here console.log(this.props.match.description); – Ilanus Sep 28 '16 at 22:32
  • One example: `<p><strong>Our Opportunity:</strong></p>` – Sergio Tapia Sep 28 '16 at 22:33
  • In this case you need either to use .innerHTML or decode HTMLEntities. – Ilanus Sep 28 '16 at 22:58
  • 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; } – Chris Adams Jan 02 '20 at 15:38
  • Regarding the name, it does not have to do with HTML entities, rather with XSS attacks. See https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml. It's dangerous if the HTML you're setting is user-supplied. – Chris Chudzicki Aug 28 '22 at 13:29
147

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.

pixelearth
  • 13,674
  • 10
  • 62
  • 110
  • 24
    Does this library use "dangerouslySetInnerHTML" in the background? – Omar Oct 09 '19 at 05:51
  • 11
    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.``` – okram Jan 02 '20 at 19:12
  • 9
    For React 17.0+, I use a similar library called "html-react-parser", which is currently supported. https://www.npmjs.com/package/html-react-parser – Marty McGee Feb 05 '21 at 14:24
  • 8
    From FAQ section of html-react-parser: "this library is not XSS (cross-site scripting) safe." https://www.npmjs.com/package/html-react-parser – Aniket Suryavanshi May 11 '21 at 10:42
  • You may need to install with : npm install react-html-parser – james.c.funk May 14 '21 at 13:52
76

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

machineghost
  • 33,529
  • 30
  • 159
  • 234
Sergio Flores
  • 5,231
  • 5
  • 37
  • 60
  • 4
    This was the issue. The description string was escaped HTML. I unescaped it and now it works fine. – Sergio Tapia Sep 29 '16 at 00:03
  • 6
    Please avoid using `dangerouslySetInnerHTML` instead use `Fragment` from react v16. Check the [next answer](https://stackoverflow.com/a/49777535/7797203) by @brad-adams – Kunal Parekh May 22 '18 at 04:42
  • 5
    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. – Brad Adams Mar 05 '19 at 22:42
  • 1
    Convert it how? With which API or package? The answer gets you halfway there. – Tsar Bomba Aug 20 '22 at 17:53
63

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>
DᴀʀᴛʜVᴀᴅᴇʀ
  • 7,681
  • 17
  • 73
  • 127
Kandarp
  • 893
  • 7
  • 8
25

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>
}
Brad Adams
  • 2,066
  • 4
  • 29
  • 38
  • 29
    There is no string containing html in your example. It's either jsx or plain string. – mrkvon Dec 19 '18 at 18:36
  • 3
    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. – Brad Adams Jan 25 '19 at 21:26
  • 2
    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 wrapped with strong` contains html tag `strong`. – Binita Bharati Nov 15 '19 at 12:39
  • 3
    @BinitaBharati But that's not a string though. If you get a string from an API like "

    This is a String

    "(or simply store a string in a variable), when you put this string in , the output will still contain the

    tag.

    – Muchdecal Mar 23 '20 at 22:53
  • Yes @Muchdecal, my answer states that this only works for "local html", not something received from an external source. – Brad Adams Mar 24 '20 at 02:58
  • 1
    @BradAdams. Nice trick though. I can see the instances where it becomes handy. – Muchdecal Mar 24 '20 at 18:20
  • @BradAdams I tried using your workaround but it did not work. The string that I am passing is coming from my server in the same app. ```const stringWithHtml = (value) => { console.log(value) return ({value}) } ``` If i replace the value to be a string with HTML in it, it does work. Any idea on resolving it? Example of my string: Hello, I am Mike. Nice to meet you – Yuvi May 28 '20 at 19:25
  • @Yuvi this solution isn't for you then unfortunately, even if the server is "in the same app". This only _works_ for strings defined within your react application (eg. in a `const`). You'll probably want to look at `dangerouslySetInnerHTML` (which isn't actually dangerous if you're using it safely) or something like `react-html-parser`. – Brad Adams May 28 '20 at 22:11
  • @BradAdams thank you for the input. I will review those two options – Yuvi May 29 '20 at 12:48
  • this worked for me, I had a side component throughout my application but had different styles and content based on the page I am viewing, so put all these in the file and read those based on the page I am viewing – Tharzeez Jun 25 '20 at 10:26
25

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>
}

UPDATE:

I removed someHTML state and added comments to make the example more coincise around the concept.

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

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

/**
 * 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) {
            spanRef.current.innerHTML = "some <b>bold</b>";
        }
    }, [spanRef]);

    return <div>
        my custom text follows<br />
        {/* ref={spanRef] will update the React.RefObject `spanRef` when html obj ready */}
        <span ref={spanRef} />
    </div>
}
Lorenzo Delana
  • 532
  • 5
  • 11
  • 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. – webhound May 15 '20 at 19:40
  • A little explanation to the answer could have done wonders. – letsbondiway Jul 27 '21 at 18:00
  • @letsbondiway see UPDATE part on my answer. – Lorenzo Delana Jul 28 '21 at 21:15
  • @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 Aug 09 '21 at 14:08
  • @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. – Lorenzo Delana Aug 12 '21 at 00:53
17

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

Hou Soune
  • 177
  • 1
  • 2
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>")
Salman Lone
  • 1,526
  • 2
  • 22
  • 29
  • Meanwhile, I do not suggest using react-render-html anymore. Its package health score is very low due to snyk: https://snyk.io/advisor/npm-package/react-render-html Html-react-parser is much better: https://snyk.io/advisor/npm-package/html-react-parser – Paul M. May 15 '22 at 12:33
1

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>);
}
Binita Bharati
  • 5,239
  • 1
  • 43
  • 24
1

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.

greenie-beans
  • 440
  • 1
  • 5
  • 15
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>)
Yanov
  • 655
  • 7
  • 13
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

MajiD
  • 2,420
  • 1
  • 22
  • 32
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>")
-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}
);
BBRay
  • 3
  • 1