Dead Simple Chat allows you to add Chat to your application using its powerful Chat API and SDK. You can add chat to any React or web application in minutes with Dead Simple Chat.
As the name suggests dangerouslySetInnerHTML
should be used cautiously. It is like the innerHTML
property that is exposed by the DOM node.
With dangerouslySetInnerHTML
you can set the HTML of the element. React does not perform any sanitization on the HTML set using dangerouslySetInnerHTML
It is called dangerouslySetInnerHTML
because it is dangerous if the HTML that is set is unfiltered or unsanitized because it exposes the risk of injecting malicious code, XSS attack and other security threats that could compromise the application.
Hence dangerouslySetInnerHTML
should be avoided unless absolutely necessary and before dangerouslySetInnerHTML
, the HTML input should be sanitized.
In this blog post, we will look at some examples of how to use dangerouslySetInnerHTML
and how to safely sanitize the HTML before setting using dangerouslySetInnerHTML
.
Basic Usage Example
Here is the basic usage example of dangerouslySetInnerHTML
import React from "react";
export default function App() {
const htmlContent = "<p>This is raw <strong>HTML</strong> content.<p>";
return (
<div className="App">
<h1>Raw HTML</h1>
<div dangerouslySetInnerHTML={{ __html: htmlContent }}></div>
</div>
);
}
In the above example, we set the raw html string stored in the variable htmlContent
The htmlContent
will be set as innerHTML
of the <div></div>
tag. We pass dangerouslySetInnerHTML
prop an object with the key __html
and the value should contain the HTML string that we want to set.
The HTML should be sanitized before being used with dangerouslySetInnerHTML
as it exposes security risks. In the above example the HTML is not sanitization and doing this not recommend as it is a bad practice and would result in sever security risks.
In the next section, we will see how to sanitize the HTML string before setting it as a value using dangerouslySetInnerHTML
.
Sanitizing with DOMPurify
In the previous section, we specified the HTML string and setting it directly to dangerouslySetInnerHTML
which is not a good practice.
In this section, we will use the package DOMPurify to santize our HTML string before using it in dangerouslySetInnerHTML
.
Let's first install the DOMPurify package using npm install
npm install dompurify
Then we will update our component to use DOMPurify:
import React from "react";
import DOMPurify from "dompurify";
export default function App() {
const htmlContent = "<p>This is raw <strong>HTML</strong> content.<p>";
const sanitizedHtmlContent = DOMPurify.sanitize(htmlContent);
return (
<div className="App">
<h1>Raw HTML</h1>
<div dangerouslySetInnerHTML={{ __html: sanitizedHtmlContent }}></div>
</div>
);
}
Using the DOMPurify library is very easy, we just need to call the santize
method on the DOMPurify library and it returns the sanitized version of the HTML.
We can then pass the sanitized version to dangerouslySetInnerHTML prop.
Verifying HTML Sanitization
To check if our DOMPurify, we will inject an XSS payload into our HTML string and see if our DOMPurify correctly escapes the HTML script.
import React from "react";
import DOMPurify from "dompurify";
export default function App() {
const htmlContent = "<script>alert(1);</script>";
const sanitizedHtmlContent = DOMPurify.sanitize(htmlContent);
return (
<div className="App">
<h1>Raw HTML</h1>
<div dangerouslySetInnerHTML={{ __html: sanitizedHtmlContent }}></div>
</div>
);
}
We have updated our htmlContent
to <script>alert(1);</script>
and if our HTML is not sanitized correctly then the page will display an alert with 1.
Apart from DOMPurify there are other sanitization libraries available, that you can use like sanitize-HTML. But when choosing a library make sure you use a library that is actively developed, has a large user base and is widely used.
Contextual escaping: Understanding when and how to use it.
Contextual escaping also called output escaping is a security technique that is used to prevent Cross site scripting attacks.
It ensures that the user generated content is safe to render in the browser. It works by using a escaping strategy that escapes certain words and char before they are rendered on the browser.
This is different than user input sanitation which sanitises the before it is being stored on the server. the contextual escaping santises data before it is being given to the browser to render
When can you use contextual escaping
- Displaying user generated context
- working with rich text
- Interpolating variable in JS
- Including data in the URL
Implementing Content Security Policy (CSP) as an additional layer of protection.
Content security policy is a security standard that allows developers to declare which dynamic resources are allowed to load, thus making it difficult for an attacker to load harmful content
This content security policy can be used to avoid a wide range of attacks such as cross site scripting and injection attacks
How to implement Content security policy
- Define the policy
Define which sources are trustworthy and can be allowed to provide scripts, styles, images.
One type of policy would be to restrict the user generated scripts.
That is only the scripts that are hosted on your servers would be allowed to run in the browser
- Using the
Content-Security-policy
HTTP header:
To implement your content security policy you need to implement the Content-Security-Policy
header with the policy definations in your responses
- Testing the policy: You can use various online tools such as the google CSP evaluator to check your policy and test out whether there are any loopholes in it
Best Practices or tips for creating your Content-Security-Policy
- Start with a strict policy and then relax as need:
To make things easier to implement, start with a strict policy, then when you see what stuff is breaking incrementally allow easier choices
- Regularly review and update the policy:
We should at regular intervals review and update the policy because as the apps get more sofisticated because of added features , more and more resources need to be whitelisted in the security policy and some resources need to be removed as well.
- Use Nonce or hashes for inline scripts:
If you are using inline scripts or css then use nonce or hashes for these.
Alternatives to Consider before using dangerouslySetInnerHTML
dangerouslySetInnerHTML should be used only when it is absolutely necessary and should be avoided whenever possible due to the security risks.
Always other options should be considered before using dangerouslySetInnerHTML, and here are some of the options that you should consider, but make sure to santize your HTML using a library like DOMPurify first:
- Try to use JSX First: You should first try to use JSX, if you have legacy code that you want to integrate, or you are integrating some 3rd party library try to use it JSX and with
refs
and only usedangerouslySetInnerHTML
as the last resort. - Use Library that converts HTML to JSX: There are multiple libraries avalaible which parses HTML into JSX, you can try to use those libraries as well, some of the popular options include:
- html-react-parser: It allows you to parse raw HTML and convert it into React elements. It is a safer alternative to
dangerouslySetInnerHTML
. However you still need to sanitize the HTML. As of writing this library has 1.6K stars on Github and 990,820 weekly downloads on npm - react-html-parser: This also allows you to convert raw HTML into react components, and it is similar to html-react-parser. It has 742 starts on github as of writing and 277k weekly download on NPM. Also thing to note that it was last updated in 2020.
Other alternatives to using dangerouslySetInnerHTML
React Portals:
React portals are a way to render children into a DOM nodes that exists outside the DOM hierarchy of the parent component.
They are not a direct alternative to directly adding HTML with dangerouslySetInnerHTML
portals can be a solution for adding components or content outside the regular component tree
server side rendering
Before using these libraries make sure to sanitize the HTML using HTML sanitization libraries like DOMPurify.
- Sanitize Content on the server:
When using server side rendering always sanitize the user generated content before it is saved and when it is sent to the client browser you only get the safe content.
- Caching Sanitized Content:
You can also cache the sanitized content to improve performance. This also reduces the need to re sanitize the content and saves resouces that are spent in sanitizing the HTML content
- Hydration considerations
When you are using the Server Side rendering the HTML generated on the server is hydrated
in a fully reactive application on the client side
You need to ensure that the dynamic content that is rendered exactly as we want it not and does not result in a mismatch between server rendered markup and what is rendered on the client side
Scenarios where dangerouslySetInnerHTML could be used
Sometimes it is inevitable to use dangerouslSetInnerHTML
and there are cases where you cannot get away without using dangerouslySetInnerHTML
, let. discuss some of those scenarios:
- HTML data coming from a Trusted Source: When your HTML content is coming from a Trusted Source like your Content Management System or from the Server Generated content. In these cases, you can use the
dangerouslSetInnerHTML
. But make sure that you trust the source of the data. - Properly Sanitized Content: You can safely use
dangerouslySetInnerHTML
content that is properly sanitized. Make sure you use a robust and well-tested sanitization library to escape any unsafe tags and XSS code. - When Integrating 3rd Party Libraries: Some 3rd Party libraries do not integrate well in React, in those cases you have to use
dangerouslySetInnerHTML
to integrate the library with your code. But before doing that make sure you trust the library and is well-vetted and does not expose your application to any security risk, and generate content is well-sanitized.
Building a Markdown Editor in React using ShowDown, DOMPurify and dangerouslySetInnerHTML
Let's build a Markdown editor, that displays that Markdown output in HTML in real-time.
To build this application we will build a component that would take markdown and convert it into HTML.
We will also use the library will work in the following manner:
- Build a React Component Accept Markdown text as a prop
- Use ShowDown to convert Markdown to HTML
- Use DOMPurify to sanitize the rendered HTML
Building MarkDownViewer
We will create a MarkdownViewer.js
component, which will accept markdown as a prop and convert it into HTML and display it on the screen.
Create a file called as src/MarkdownViewer.js
and add the following code:
import showdown from "showdown";
import DOMPurify from "dompurify";
import React from "react";
function MarkdownViewer({ md, styles, className }) {
const converter = new showdown.Converter();
const html = converter.makeHtml(md);
const sanitizedHTML = DOMPurify.sanitize(html);
return (
<div
styles={styles}
className={className}
dangerouslySetInnerHTML={{ __html: sanitizedHTML }}
></div>
);
}
export default MarkdownViewer;
In the above code, we have created a MarkdownViewer
component, we have first imported the dependencies showdown
and dompurify
.
You can install them using npm
npm install showdown
npm install dompurify
Then we are creating a converter
object and converting markdown to html.
const html = converter.makeHtml(md);
Then we are sanitizing the generated HTML using the DOMPurify
library:
const sanitizedHTML = DOMPurify.sanitize(html);
Finally, we set the generated HTML as dangerouslySetInnerHTML
to the div tag
return (
<div
styles={styles}
className={className}
dangerouslySetInnerHTML={{ __html: sanitizedHTML }}
></div>
);
Building the Editor
The Editor Component is very simple, it will contain a textarea and will accept onChange
method as a prop.
Create a file called as src/Editor.js
to hold our Editor Component.
We will call the onChange method from the prop when the value of the textarea changes.
export default function Editor({ onChange, styles, className }) {
return (
<textarea
styles={styles}
className={className}
onChange={onChange}
></textarea>
);
}
Putting it all together
Now, let's open our src/App.js
file and import the MarkdownViewer and Editor components.
We will attach the onChange listener to the Editor component, get the value from the Editor and set it as a prop to the MarkdownViewer component to display the Markdown typed by the user.
import React, { useState } from "react";
import Editor from "./Editor";
import MarkDownViewer from "./MarkdownViewer";
import "./styles.css";
export default function App() {
const [editorValue, setEditorValue] = useState("");
function handleOnChange(event) {
setEditorValue(event.target.value);
}
return (
<div className={"container"}>
<Editor className={"half"} onChange={handleOnChange} />
<MarkDownViewer className={"half"} md={editorValue} />
</div>
);
}
In the above cover, we have created a state variable called as editorValue
and created a method called as handleOnChange
.
We are passing the handleOnChange
method as a prop to the Editor component, when the value changes in the Editor Component, we are updating the editorValue
when textarea changes.
Then we are passing editorValue
to the Markdown viewer component.
Demo
Here is the Demo of our Markdown Editor
Performance impact of using
Metered TURN servers
- Global Geo-Location targeting: Automatically directs traffic to the nearest servers, for lowest possible latency and highest quality performance. less than 50 ms latency anywhere around the world
- Servers in 12 Regions of the world: Toronto, Miami, San Francisco, Amsterdam, London, Frankfurt, Bangalore, Singapore,Sydney (Coming Soon: South Korea, Japan and Oman)
- Low Latency: less than 50 ms latency, anywhere across the world.
- Cost-Effective: pay-as-you-go pricing with bandwidth and volume discounts available.
- Easy Administration: Get usage logs, emails when accounts reach threshold limits, billing records and email and phone support.
- Standards Compliant: Conforms to RFCs 5389, 5769, 5780, 5766, 6062, 6156, 5245, 5768, 6336, 6544, 5928 over UDP, TCP, TLS, and DTLS.
- Multi‑Tenancy: Create multiple credentials and separate the usage by customer, or different apps. Get Usage logs, billing records and threshold alerts.
- Enterprise Reliability: 99.999% Uptime with SLA.
- Enterprise Scale: With no limit on concurrent traffic or total traffic. Metered TURN Servers provide Enterprise Scalability
- 50 GB/mo Free: Get 50 GB every month free TURN server usage with the Free Plan
- Runs on port 80 and 443
- Support TURNS + SSL to allow connections through deep packet inspection firewalls.
- Support STUN
- Supports both TCP and UDP
You might be interested in some of our other articles
- React Server Components with Next.JS: The Complete Guide
- What are Content Moderators?
- React useRef: A complete guide with examples
- React useState: The Complete guide
Conclusion
In this blog post, we have learned what dangerouslySetInnerHTML
is, and how to safely use it in our application.
We have also looked at ways to avoid using dangerouslySetInnerHTML
and its alternatives and also looked at ways to safely escape the HTML code before rendering it using dangerouslySetInnerHTML
.