Do Client Components in Next.js Really Run Only on the Client?

nextjsclient-componentsweb-developmenthydration-errorhydration

After you complete this article, you will have a solid understanding of:

  • Where client components are actually rendered.
  • Why client components aren't just rendered on the client, and what the benefits are.
  • What hydration is, what a hydration error is, and how to fix one.

There are two types of components in Next.js

  1. Server Components
  2. Client Components

Server components are relatively easy to understand (okay, maybe not). They are rendered only on the server and sent to the client as a static HTML file.

But what happens with client components? Do they only render on the client? Why do we even need client components? How are they useful?

I know you have many questions in your mind, but let's take it step by step.

Have You Ever Encountered a Hydration Error?

If you're lucky and have never seen one, let's take a look at what a hydration error looks like. This error will actually help you understand some concepts even more clearly.

Try running this code in your editor:

"use client";
const MyComponent = () => {
  const currentTime = new Date().getTime();
  return <div>{currentTime}</div>;
};

And, you will see;

Hydration Error

"Server-rendered HTML didn't match the client."

Wait, what? Why was the HTML rendered on the server even though it was a client component? Isn't it supposed to be rendered on the client? How is this different from server components if everything is rendered on the server?

Let's start by understanding what hydration actually is.

What is Hydration?

Hydration → The process of taking pre-rendered HTML (usually generated on the server) and enhancing it with client-side JavaScript to enable interactivity.

So, hydration gives our HTML a superpower. Thanks to the hydration process, we're not just reading a plain HTML document coming from the server, but we're also able to interact with it.

When we create a client component in Next.js, first, a pre-rendered HTML is created for that component on the server side—but nothing else.

No JavaScript code, no React useState calls, no useEffect calls. It's just the HTML code.

After we receive that HTML on the client side, we are ready to start the hydration process. At this point, we get our useState calls, useEffect, or any other interactivity used in our component.

Okay. But can we render everything on the client? I don't want to get involved with hydration or any other stuff!

Of course, we can. But there are downsides.

Benefits of Server-Side Rendering Before Hydration

1.Faster Initial Load

If all Client Components ran only on the client, you would see a blank screen while waiting for JavaScript to load and execute.

2.Better SEO

A fully client-rendered page can cause SEO issues, as search engines may struggle to index content properly.

3.Network Optimization

Server-rendered HTML has a smaller bundle size and better caching opportunities at the CDN level.


Now we know why rendering the HTML part on the server and sending the JavaScript code afterward (a.k.a hydration) is a better approach in most cases.

Common Causes of Hydration Errors

"use client";
const MyComponent = () => {
  return (
    <p>
      <div></div>
    </p>
  );
};

In a case like this, the browser might try to fix the incorrectly placed elements. If that happens, the final HTML won't match React's Virtual DOM, leading to a hydration error.

Another common cause is trying to access the window object inside a client component. Since window doesn't exist on the server, it causes hydration issues.

"use client";
const MyComponent = () => {
  // This will cause a hydration error
  const screenWidth = window.innerWidth;
  return <div>Your screen width is: {screenWidth}px</div>;
};

In this example, trying to access window.innerWidth during server rendering will fail because the window object doesn't exist on the server. This causes the component to render differently on the server versus the client.

"use client";
const MyComponent = () => {
  // This will cause a hydration error
  const currentTime = new Date().getTime();
  return <div>{currentTime}</div>;
};

The issue here is that new Date().getTime() will return different values on the server and client, causing a mismatch between server-rendered HTML and client-side rendering.

"use client";
const MyComponent = () => {
  // This will cause a hydration error
  const randomValue = Math.random();
  return <div>Random value: {randomValue}</div>;
};

Using Math.random() generates different values on the server and client, leading to a hydration error since the server-rendered content doesn't match what's generated on the client.

"use client";
const MyComponent = () => {
  // This will cause a hydration error
  useEffect(() => {
    // This runs after initial render
    document.getElementById("my-element").innerHTML = "Changed content";
  }, []);

  return <div id="my-element">Initial content</div>;
};

Direct DOM manipulation inside a useEffect can cause hydration errors if it runs too early and changes the DOM before React has finished hydrating the component.

Opting Out of Server-Side Rendering

What if we want to render a component only on the client? Is there a way to do that?

There is always a way for everything!

import dynamic from "next/dynamic";

const MyComponent = dynamic(() => import("../components/MyComponent"), {
  ssr: false,
});

export default function Page() {
  return <MyComponent />;
}

This way, MyComponent will only render on the client, and Next.js won't attempt to pre-render it on the server.

Now you fully understand how Client Components work in Next.js!

Was this blog helpful for you? If so,

Be the first to know when new blog drop. No ads, no BS. ~once a week.