Fix 'dangerouslySetInnerHTML did not match' in React
By Flavio Copes
Learn how to fix the React dangerouslySetInnerHTML did not match warning, caused by nesting a p tag inside another p tag, by switching the wrapper to a div.
The dangerouslySetInnerHTML did not match warning shows up when the HTML React renders on the server is different from the HTML it generates in the browser. In my case, the cause was a p tag nested inside another p tag. The fix was to use a div as the wrapper.
Here’s the full story.
I was trying to print the HTML contained in a prop, using dangerouslySetInnerHTML, while I got this error in the browser console:
Warning: Prop `dangerouslySetInnerHTML` did not match.
This was a Next.js project, but the solution applies to any React code that uses server-side rendering.
The string I was trying to print appeared for a while, and then disappeared. Quite strange!
It was even stranger when I tried to print a fixed HTML string, like this:
<p
dangerouslySetInnerHTML={{
__html: '<p>test</p>'
}}></p>
The error message is cryptic but after a while, I realized I could not set a p tag inside another p tag.
Switching to:
<div
dangerouslySetInnerHTML={{
__html: '<p>test</p>'
}}></div>
worked like a charm.
Why does this happen?
HTML does not allow a p element inside another p element. When the browser parses the server-rendered page and finds the inner <p>, it closes the outer one first. The DOM it builds is not the DOM React expects.
Then React runs in the browser and hydrates the page. It compares the server HTML with what it would render on the client. The two don’t match, so React logs the warning and re-renders that part of the tree. That’s why my content flashed on screen and then disappeared.
The same rule applies to other invalid nesting too. A div inside a p causes the same problem, and so does a p inside a span.
Other causes of the same warning
Invalid nesting is not the only trigger. Any difference between the server render and the client render produces a hydration mismatch.
A common one is rendering something that changes between the two environments, like the current time or a random value. If the server renders one string and the browser renders another, you get the same warning.
The fix in that case is to make sure both renders produce the same output, for example by generating the value once and passing it down as a prop.
But if you’re using dangerouslySetInnerHTML and see this warning, check the wrapper element first. Nine times out of ten, it’s a p wrapping block-level content. Switch it to a div and you’re done.