---
title: "Next.js Hydration Window Issue"
description: "The Next.js hydration window issue throws \"window is not defined\" even in a client component. Why hydration causes it and how to fix it with usePathname."
canonical: https://omiid.me/notebook/20/next-js-hydration-window-issue
author: Omid Sayfun
published: 2024-05-29
updated: 2026-08-09
---

# Next.js Hydration Window Issue

Coming from a React environment, I wrote a page view tracker like this:

```typescript
"use client";
import React from 'react';

export const TrackPageView = () => {
	React.useEffect(() => {
		trackView({
			url: window.location.href,
			referrer: document.referrer,
		});
	}, [window.location.href, document.referrer]);

	return <></>;
}
```

But this doesn’t work in Next.js. If you test it out, you would either get:

```shell
ReferenceError: window is not defined
```

which is clear enough, or you might encounter:

```shell
TypeError: Response body object should not be disturbed or locked
```

This happens because you’re calling the `trackView` server action and the signal is aborted. The `TypeError: Response body object should not be disturbed or locked` message comes from that aborted request, not from your tracking code. Both errors trace back to Next.js hydration. Even though the component is marked as a client component, hydration also renders it during the server pre-render.

## Next.js hydration makes server-rendered HTML interactive

Hydration in Next.js is the process that makes server-rendered HTML interactive on the client side. Next.js produces static HTML during the server-side rendering (SSR) phase. Once the JavaScript loads and runs in the browser, React attaches to that HTML and the page becomes a fully interactive app.

### Hydration buys you SEO, first-load speed, and interactivity

Hydration offers several advantages:

1. **SEO and Performance**: By rendering the initial HTML on the server, Next.js ensures that search engines can index the content, improving SEO. Additionally, the initial load time is faster because the user receives a fully-rendered page almost immediately.
2. **Interactivity**: Once the static HTML is hydrated, the page becomes interactive, allowing for a dynamic user experience with all the benefits of React.
3. **Consistency**: Hydration ensures that the server-rendered HTML and the client-side React components are synchronized, providing a stable user experience.

### Hydration errors start where your code assumes a browser

While hydration is beneficial, it comes with its own set of challenges:

1. **Window is Not Defined**: During server rendering there is no `window` object, so `ReferenceError: window is not defined` is thrown as soon as your code reads it. `window` is a browser-specific global object, and it does not exist in the Node.js environment where the server-side code runs.
2. **Hydration Mismatches**: If the server-rendered HTML does not match the HTML generated by React on the client, hydration can fail, causing UI glitches or errors.
3. **Performance Overhead**: Hydration can add a performance overhead because the browser needs to parse and execute the JavaScript code to make the page interactive.

## Read the path from usePathname instead of window

Swap `window.location.href` for the `usePathname` hook from `next/navigation`, and drop `document.referrer` from the dependency array:

```typescript
"use client";
import React from "react";
import { usePathname } from "next/navigation";

export const TrackPageView = () => {
	const pathname = usePathname();

	React.useEffect(() => {
		trackView({
			url: pathname,
			referrer: document.referrer,
		});
	}, [pathname]);

	return <></>;
};
```

Removing `document.referrer` from the dependency array matters as much as the hook swap. Everything inside `useEffect` runs on the client only. The dependency array does not get that guarantee, so during hydration you can still get an error saying `document` is not defined. With `pathname` as the only dependency, the effect runs once per navigation and never reads a browser global on the server.

`suppressHydrationWarning` does not help here. It silences a hydration mismatch warning on a single element, and it does not give the server a `window` object.

## Food for thought

Hydration is React attaching to HTML the server already rendered. Code that reads `window` or `document` has to run on the client after that, usually in an effect.

When working with Next.js:

1. Be cautious with direct `window` and `document` usage.
2. Understand the hydration process and its implications.
3. Use Next.js hooks and utilities to manage client-side interactions effectively.
