How to set InnerHTML content in React

How to set InnerHTML content in React

ยท

3 min read

In this blog, we are going to discuss how to make use of innerHTML in React Applications in a secure way.

Consider a scenario where the user input is HTML tags and the application must display according to the input given by the User. In the following code examples, I have considered a bio input for the user profile.

Let's try using the regular way of taking input a string and displaying using useState Hook

import { useState } from "react";
export default function WithoutInnerHTML() {
  const [bio, setBio] = useState("");
  const [userBio, setUserBio] = useState("");
  const handleChange = (e) => {
    setBio(e.target.value);
  };
  const handleSubmit = () => {
    setUserBio(bio);
  };
  return (
    <div>
      <label>Bio : </label>
      <textarea
        placeholder="Enter some html tags"
        type="text"
        value={bio}
        onChange={handleChange}
      />
      <button onClick={handleSubmit}>Submit</button>
      <div>{userBio}</div>
    </div>
  );
}

withoutInnerHTML output.png

It is being displayed as a regular string instead of parsing the HTML tags and displaying them

How to do it.gif

Now lets try directly accessing the innerHTML property in React

 <div innerHTML={formattedMessage}></div>

If we try using the above syntax it gives the following error

error.png

Is there any way of using innerHTML mentioned in the documentation

tadaa.gif

YES, It is mentioned that with the help of dangerouslySetInnerHTML property on a HTML tag we can set the innerHTML content Documentation

Code Example :

export default function InnerHTML() {
  const [bio, setBio] = useState("");
  const [userBio, setUserBio] = useState("");
  const handleChange = (e) => {
    setBio(e.target.value);
  };
  const handleSubmit = () => {
    setUserBio(bio);
    setBio("");
  };
  return (
    <div>
      <label>Bio : </label>
      <textarea
        type="text"
        value={bio}
        placeholder="Enter some html tags"
        onChange={handleChange}
      />
      <button onClick={handleSubmit}>Submit</button>
      <div dangerouslySetInnerHTML={{ __html: userBio }}></div>
    </div>
  );
}

But Is this the secure way to do it?

thinking.gif

Try putting the following code and submit

<img src=?? onerror="alert(`You are hacked ๐Ÿ˜`)" />

Once you submit the above code it results in an alert.

alert.png

So we can say that the user is able to run a javaScript code on our webpage which can lead to a Cross-Site Scripting Attack

How to prevent such things ?

how.gif

We can prevent the execution of such code input with the help of an npm package called DOMpurify

Final code using DOMpurify sanitize function :

import DOMpurify from "dompurify";
export default function WithDOMpurify() {
  const [bio, setBio] = useState("");
  const [userBio, setUserBio] = useState("");
  const handleChange = (e) => {
    setBio(e.target.value);
  };
  const handleSubmit = () => {
    setUserBio(bio);
    setBio("");
  };
  return (
    <div>
      <label>Bio : </label>
      <textarea
        type="text"
        value={bio}
        placeholder="Enter some html tags"
        onChange={handleChange}
      />
      <button onClick={handleSubmit}>Submit</button>
      <div
        dangerouslySetInnerHTML={{ __html: DOMpurify.sanitize(userBio) }}
      ></div>
    </div>
  );
}

Now if we try to execute the same code which resulted in an alert, It doesn't show any alert. It's because of the DOMpurify package. If you can inspect and see the output the DOMpurify.sanitize function removes the onerror property of the img tag.

Summary :

If we want to display some user input HTML on the react use dangerouslySetInnerHTML along with the DOMpurify.sanitize function. CodeSandBox with all the above codes.

ย