Hi, I'm Tuan, a Full-stack Web Developer from Tokyo 😊. Follow my blog to not miss out on useful and interesting articles in the future.
1. Introduction
I've been working with Next.js for about three months in my job. During that time, I encountered an error during development: "Text content does not match server-rendered HTML." Although solving the issue wasn't difficult, the solution I used was different from what I found through Google or what ChatGPT suggested. In this article, I will introduce the general solutions and the one I used, showing that simply searching for error messages might not always lead you to the best solution. Even if you're not familiar with React, you should be able to understand the overall idea.
2. The Code that Caused the Error
Here is a simplified version of the code that caused the error. This component uses Server-Side Rendering (SSR) to render the content. The code is designed to display a comment as-is when it's within 30 characters, and truncate it with "..." if it exceeds 30 characters.
function MyComponent({comment}) { const displayedComment = comment.length > 30 ? comment.slice(0, 30) + '...' : comment return <div>{displayedComment}</div>
}
3. The Error and General Solutions
The error "Text content does not match server-rendered HTML" occurs when there's a discrepancy between server-side and client-side rendering. Let's look at some common solutions found through Google or provided by ChatGPT.
3.1. Avoid using browser-only libraries or application code
window
is only undefined
on the server-side, so using it can cause a discrepancy between server and client rendering. One solution is to use useEffect()
which is called after rendering.
import { useEffect, useState } from 'react'
function MyComponent() { const [color, setColor] = useState('blue') useEffect(() => setColor('red'), []) return <h1 className={`title ${color}`}>Hello World!</h1>
}
3.2. Use Dynamic Import for components you don't want to render server-side
If you don't want to render a component server-side, you can use the Dynamic Import feature.
import dynamic from 'next/dynamic' const DynamicHeader = dynamic(() => import('../components/header'), { ssr: false,
})
3.3. Correct the HTML structure according to content model rules
HTML tags have parent-child relationship limitations called content models. For example, using a div
tag as a child of a p
tag is incorrect. You can solve this by replacing the p
tag with a div
tag.
export const CorrectComponent = () => { return ( <div> <div> This is correct and should work because a div is really good for this task. </div> <Image src="/vercel.svg" alt="" width="30" height="30" /> </div> )
}
3.4. Add an attribute to ignore the error
You can also use a JSX attribute to suppress the error, as shown in React's official documentation.
export default function App() { return ( <h1 suppressHydrationWarning={true}> Current Date: {new Date().toLocaleDateString()} </h1> );
}
4. The Solution I Adopted
In my case, the error occurred when a comment was truncated right at an emoji or a specific kanji character. These characters are represented by a combination of two special Unicode code points called surrogate pairs. When one of these pairs was cut off by .slice()
, the error occurred. While the general solutions mentioned earlier did eliminate the error, they didn't fix the character corruption issue. The best solution was to adjust the character count logic to account for surrogate pairs. I used an array conversion method to handle this.
function MyComponent({comment}) { const displayedComment = [...comment].length > 30 ? [...comment].slice(0, 30).join('') + '...' : comment return <div>{displayedComment}</div>
}
However, this method still does not solve a case like the one below. If you have a better solution, please let me know.
const str = '👨👩👧👦';
[...str].slice(0, 5);
// Result: [ "👨", "", "👩", "", "👧" ]
Conclusion
This simple example demonstrates that the best solution for an error is not always the one found by searching the error message. Of course, it's essential to try the solutions you find, but understanding why the error occurs is also crucial. I hope this article helps you in your error-solving journey!
And Finally
As always, I hope you enjoyed this article and got something new. Thank you and see you in the next articles!
If you liked this article, please give me a like and subscribe to support me. Thank you. 😊