For cases when we need to render an html string from code (i.e. string returned from translation function or from API), React provides us with dangerouslySetInnerHTML property, which can be used like this:
<div dangerouslySetInnerHTML={{ __html: t(`legal:tos`), }} />
However, documentations warns us:
In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack.
React docs
It means that this method is safe to use only if html was generated by your code and not provided by a 3rd-party (i.e. user). For example, if some of your translation strings may contain html code, using dangerouslySetInnerHTML
is completely fine.
But sometimes you need to render html string that came non-trusted source, i.e. entered by user via WYSIWYG editor. In this case using dangerouslySetInnerHTML
may open your application for XSS attacks.
To solve this problem, you need to sanitize such string before passing it to dangerouslySetInnerHTML
. I use sanitize-html package to do it. Example of component that sanitizes and renders string that contains html:
import React from "react"; import SanitizeHTML from "sanitize-html"; export const SafeHTML = ({ className = "", children }) => { const unsafe = React.Children.toArray(children) .filter((child) => typeof child === "string") .join(""); let escaped = SanitizeHTML(unsafe, { allowedTags: [ "blockquote", "p", "a", "ul", "ol", "li", "b", "i", "strong", "em", "strike", "del", "br", "div", "sup", "sub", ], allowedAttributes: { a: ["href", "name", "target"], img: ["src"], }, // Lots of these won't come up by default because we don't allow them selfClosing: [ "img", "br", "hr", "area", "base", "basefont", "input", "link", "meta", ], // URL schemes we permit allowedSchemes: ["http", "https", "ftp", "mailto"], allowedSchemesByTag: {}, }); return ( <div className={className} dangerouslySetInnerHTML={{ __html: escaped, }} /> ); };
Sanitize-html has a lot of options to configure what tags and attributes are allowed. Refer to package’s documentation for details. In example above I allow only subset of html tags that I consider to be XSS-safe.
Usage:
<SafeHTML className={"space-y-2 sm:space-y-4"}> {someUnsafeHTMLInThisVariable} </SafeHTML>
Featured image source – pxhere.com