Hotjar for Next.js
Load Hotjar only after analytics consent, and delete its cookies when consent is withdrawn.
hotjar() loads Hotjar after the visitor grants the analytics category. Before that, nothing is loaded and nothing is stored, not even in memory. When consent is withdrawn, it deletes Hotjar's cookies, storage and widgets, and Hotjar stops on the next page load.
It loads the classic Hotjar tracking code, static.hotjar.com/c/hotjar-<siteId>.js. Hotjar accounts are moving to Contentsquare. If your account gives you a Contentsquare tag instead, this integration does not manage it.
Set it up
Install the integrations package and add hotjar() to the initCookieYes() call you already have. The site ID is the hjid value in the snippet Hotjar gives you:
npm install @cookieyes/scripts"use client";
import { initCookieYes } from "@cookieyes/nextjs";
import { hotjar } from "@cookieyes/scripts";
initCookieYes({
mode: "cookie-only",
regulation: "GDPR",
integrations: [hotjar({ siteId: 1234567, reloadOnRevoke: true })],
});Recommended: set reloadOnRevoke: true
Hotjar has no stop command. After consent is withdrawn, it keeps recording until the page reloads. reloadOnRevoke: true reloads the page at once, so recording stops when the visitor says no.
To let the visitor choose when to reload instead, mark Hotjar as needing a reload before initCookieYes(). The SDK reads this list once, when it starts, so a line placed after it misses returning visitors:
"use client";
import { initCookieYes, registerStopHandler } from "@cookieyes/nextjs";
import { hotjar } from "@cookieyes/scripts";
registerStopHandler({ id: "hotjar", category: "analytics", needsReload: true });
initCookieYes({
mode: "cookie-only",
regulation: "GDPR",
integrations: [hotjar({ siteId: 1234567 })],
});Then render <ReloadNotice />. It appears when a visitor withdraws analytics after Hotjar ran.
Moving from builtInIntegrations: [{ vendor: "hotjar" }]? That showed the reload notice on withdrawal. Add the line above to keep it.
Remove the Hotjar snippet from your page. The integration adds the script itself.
Send events and user details through these functions. They do nothing before consent or after withdrawal, and never throw:
import { hotjarEvent, hotjarIdentify } from "@cookieyes/scripts";
hotjarEvent("signup");
hotjarIdentify("user_123", { plan: "pro" });Calls made before consent are dropped, not kept for later. In development, the first dropped call logs one console message.
Options
| Option | Default | What it does |
|---|---|---|
siteId | required | The hjid value from your Hotjar snippet |
reloadOnRevoke | false | Reload the page when consent is withdrawn, so recording stops at once |
snippetVersion | 6 | The hjsv value from your Hotjar snippet |
scriptUrl | https://static.hotjar.com/c/hotjar-<siteId>.js?sv=<snippetVersion> | Load Hotjar's script from another URL |
category | "analytics" | The category the visitor must grant. Session recording is more sensitive than ordinary analytics, so consider a category of its own |
id | "hotjar" | Only needed when you run more than one site |
Good to know
- Without
reloadOnRevoke, withdrawal is not immediate. The cookies, storage and widgets go at once, but recording continues until the next page load. In a single-page app, Hotjar can also set its_hjSession_<siteId>cookie again on the next route change. - Accepting again on the same page continues the session in memory. No second copy is loaded. A new session starts on the next page load.
- Tell visitors about recordings. "Analytics" sounds like page counts, but Hotjar records clicks, mouse movement and scrolling. Say so in your analytics category description.
- Hide sensitive content. Add
data-hj-suppressto any element whose text, images or video must stay out of recordings. - Global Privacy Control. For CCPA visitors whose browser sends it, every non-required category starts off, so Hotjar does not load until they opt in. That is the default;
region.honorGpc: falseturns it off. Hotjar honours Do Not Track by itself. - Contentsquare tag. If your Hotjar account has moved to Contentsquare, Hotjar's script can also load a Contentsquare tag. This integration does not manage that tag.
Check it works
Open your site in a private window and watch the Network tab and the cookies:
- Before a decision: no request to
hotjar.com, and no cookie starting with_hj. - Accept: Hotjar loads, and
_hjSession_<siteId>and_hjSessionUser_<siteId>appear. - Withdraw consent in the preferences dialog: the
_hjcookies are gone. WithreloadOnRevoke: true, the page reloads. - After a reload: no request to
hotjar.com.
The devtools panel shows the integration as active, and removed after a withdrawal.
Common mistakes
Hotjar loads before the visitor accepts. The Hotjar snippet is still in your page. Remove it.
hj is not defined in the console.
Your code calls hj() before consent. Use hotjarEvent() and hotjarIdentify() instead.
Page views are counted twice in a single-page app.
Hotjar tracks URL changes by itself. Call hotjarStateChange() only if you switched Hotjar's URL tracking to manual.
Next steps
- Overview: the other integrations
- Custom integration: a tool with no ready-made integration