The Next.js Hydration error and Dynamic imports
Originally written on
One of my first freelance projects was playade.rs. And working with musicians and artists, it's certain you're gonna need to embed some external media, like YouTube or SoundCloud. That is how I first ran into a typical Next.js issue: the Next.js hydration error.
If you’ve worked with Next.js, you’ve probably seen this error before. It’s the one where the browser complains that "Hydration failed because the initial UI does not match what was rendered on the server". It took me a while to figure out what was going on, but I eventually found a solution using dynamic imports.
The problem: why does the hydration error happen?
Next.js, especially in version 13 with the Pages Router, is built around server-side rendering (SSR) by default. This means that when a user requests a page, Next.js generates the HTML on the server and sends it to the browser. Once the browser receives the HTML, React "hydrates" it by attaching event listeners and making it interactive.
The problem arises when there’s a mismatch between what’s rendered on the server and what React tries to hydrate on the client. This mismatch can happen when you use libraries that depend on the browser environment (like window or document).
Since the server doesn’t have access to the browser environment, it can’t render these libraries properly. As a result, the HTML generated on the server doesn’t match what React expects on the client, and you get a hydration error.
The solution: Dynamic imports
To fix the hydration error, I used dynamic imports with ssr: false. This tells Next.js to skip server-side rendering for the component and only load it on the client. Here’s how I implemented it for react-player:
1import dynamic from 'next/dynamic';23const ReactPlayer = dynamic(() => import('react-player/lazy'), { ssr: false });
This ensures that react-player is only loaded in the browser, where it has access to the window object. No more server-side rendering, no more hydration errors.
I also needed to apply this to react-photo-gallery, not just react-player.
Why Dynamic imports work
Dynamic imports are perfect for cases like this because they let you defer loading a component until it’s actually needed. By disabling SSR (ssr: false), you avoid trying to render browser-dependent code on the server. It’s a clean, simple solution for external libraries that aren’t SSR-friendly.
Author's note (2025)
In newer versions of Next.js (13.4+), the App Router introduced the "use client" directive, which makes handling hydration errors much easier. Instead of relying on dynamic imports, you can simply add "use client" at the top of a file to mark it as client-rendered. This eliminates the need for ssr: false in many cases.
However, if you’re still using the Pages Router or working with older projects, dynamic imports remain a reliable solution.