Coming from a React environment, I wrote a page view tracker like this:
"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:
ReferenceError: window is not definedwhich is clear enough, or you might encounter:
TypeError: Response body object should not be disturbed or lockedThis 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.
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 offers several advantages:
While hydration is beneficial, it comes with its own set of challenges:
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.Swap window.location.href for the usePathname hook from next/navigation, and drop document.referrer from the dependency array:
"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.
Hydration is a powerful feature of Next.js that bridges the gap between server-rendered and client-rendered applications. While it brings many benefits, it also requires careful handling of client-side code.
In summary, when working with Next.js:
window and document usage.Occasional notes on software, tools, and things I learn. No spam.
Unsubscribe anytime.