Part 27: Understanding Client-Side Hydration in Next.js

[Pages] Client-side Functionality

[Pages] Client-side Functionality

In our journey to build a dynamic web application using Next.js, we've implemented a feature to toggle "Dark Mode" with a simple button click. This involves running JavaScript code in the browser to manage the UI state, but there's more to it when considering how Next.js operates. Let's delve into how Next.js handles rendering and the concept of client-side hydration.

Server-Side Rendering and the Initial Page Load

Next.js is renowned for its ability to pre-render pages on the server. This means that when you navigate to a page, the server generates the HTML and sends it to the browser. This initial rendering includes all static content and components, like our "ThemeSwitch" button for toggling dark mode.

Pre-Rendering Explained

When you run your application in development mode and reload a page, the server re-renders it, including all components, each time. This is part of what makes development in Next.js feel dynamic and responsive. However, in production mode, things work differently. By running the command npm run build, Next.js generates static HTML files for each page. These files are then served directly to the browser, reducing server load and improving page load times.

The Role of Hydration

When your Next.js app runs in production, and you navigate to a page, the server sends the pre-rendered HTML. However, this HTML alone doesn't include the interactive functionality of your React components. That's where hydration comes in.

What is Hydration?

Hydration is a process where React takes over the static HTML and attaches the interactive functionality of your components. When the browser loads a page, it first fetches the static HTML. At this point, components like "ThemeSwitch" are mere static elements without any interactivity. Once the necessary JavaScript files are loaded, React initializes the app and "hydrates" these components, attaching event handlers and making them interactive.

Example: Hydrating the ThemeSwitch Component

// components/ThemeSwitch.js

import { useState } from 'react';
import DarkTheme from './DarkTheme';

function ThemeSwitch() {
  const [darkMode, setDarkMode] = useState(false);

  return (
    <>
      <button onClick={() => setDarkMode(!darkMode)}>
        {darkMode ? 'Switch to Light Mode' : 'Switch Dark Mode'}
      </button>
      <style jsx>{`
        button {
          background: none;
          border: none;
          color: inherit;
          cursor: pointer;
        }
      `}</style>
      {darkMode && <DarkTheme />}
    </>
  );
}

export default ThemeSwitch;

During the hydration process, React calls the ThemeSwitch function again, this time attaching event listeners like onClick to the button, allowing you to toggle between light and dark modes.

The Double Rendering Phenomenon

One might wonder why components seem to render twice—once on the server and again on the client. This dual rendering is essential for a seamless user experience:

  1. Server-Side Rendering (SSR): Provides fast initial load times by serving static HTML, improving SEO and performance.

  2. Client-Side Hydration: Enables interactivity by executing JavaScript on the browser, allowing components to manage state and respond to user actions.

Experiment: Disabling JavaScript

To understand the importance of hydration, try disabling JavaScript in your browser. Reload a page, and you'll see the static content, but interactive features like the "Dark Mode" button won't function. This illustrates the necessity of hydration for client-side interactivity.

Conclusion

In Next.js, the combination of server-side rendering and client-side hydration offers the best of both worlds: fast, SEO-friendly initial loads and rich interactive experiences. By understanding this process, you can better optimize your applications and enhance user experiences. With the "Dark Mode" toggle, we've seen firsthand how these concepts play out, ensuring our app is both performant and interactive.

Last updated