User ID
Learn how to pass a custom user identifier to improve cross-session tracking accuracy.
Overview
A user identifier helps marketing platforms to stitch together a unique user's journey across multiple sessions and prevents inflated user counts.
Elevar's auto-generated user ID relies on browser storage, which can have a lifetime of just 7 days depending on browser settings. This short lifetime can cause:
- User count inflation: One user appears as multiple users in GA4 after 7 days
- Broken journey tracking: User sessions don't connect across longer time periods
- Session enrichment gaps: Elevar can't recognize returning users and enrich data with full user identifiers
Passing your own user ID avoids these issues. The durable way to do it is to mint a first-party cookie on the server (or at your CDN edge), then read that cookie in the browser and hand the value to Elevar.
TIP
Implementing a custom user ID is a best practice for every API implementation, headless and agnostic alike, and ensures accurate user tracking and optimal performance of Elevar's Session Enrichment feature.
How to Pass Your Own User ID
Pass User ID:
You can provide your own user ID by defining a global function before the Elevar snippet runs. This code must execute before the Elevar snippet that you copied from the Elevar app. If the Elevar script runs first, your custom user ID will not be recognized.
const getMyUserId = async () => Promise.resolve("my_custom_user_id");
window.ElevarUserIdFn = async () => {
const userId = await getMyUserId();
return userId;
};WARNING
If you run the Elevar script first, your custom user ID will not be recognized. The function must be defined on every page, this doesn't include virtual page changes. The window.ElevarUserIdFn must return a string or a promise to a string.
Verify the User ID:
Once implemented, any Data Layer event will include your custom user ID. You can verify this by inspecting the most recent Elevar Data Layer event:
console.log(window.ElevarDataLayer.at(-1)?.marketing?.user_id);Setting a Durable Cookie
window.ElevarUserIdFn is only as durable as the value you return from it. To get an ID that survives Safari's 7 day cap on script-written storage, the cookie has to be set by an HTTP response rather than by JavaScript.
The rules below apply to every example in this guide.
WARNING
- Set the cookie on the top-level document response. A cookie set on the HTML response of a top-level navigation is exempt from Safari's expiry cap.
- Do not use a subdomain to mint it. A dedicated host such as
id.example.comcan be treated as third-party and have its cookie capped, and whether that happens varies per visitor. - Never rewrite the cookie from JavaScript. Any
document.cookiewrite is treated as script-written and silently downgrades a one year cookie to 7 days. All refreshes must happen over HTTP. - Use
SameSite=Lax, notStrict.Strictbreaks ad-click landings, where the visitor arrives from another domain. - Re-send the cookie on every document response. Cookie expiries are fixed points in time, so a browser only extends it when a response sends it again. Re-sending it will keep a returning visitor's ID alive for longer. Make sure that the value remains the same if the cookie was present on the request.
- Do not set
HttpOnly. Yourwindow.ElevarUserIdFnlogic won't be able to see the cookie if you do.
Implementation Examples
Defining the User ID Function in Next.js:
If you have used the following to load the Elevar snippet using Next.js application or inline scripts, define window window.ElevarUserIdFn at the top of the same script that loads the Elevar snippet.
If you have added our snippet via a useEffect at the top level of your app (ensuring that it only fires once per hard page load), you can either define window.ElevarUserIdFn at the top of that effect, or outside of the component that calls that effect (but in the same file).
Server Setting the User ID From Next.js:
Server setting the user ID from Next.js can be achieved with the proxy.js|ts file. Follow this guide to learn more.
import { NextResponse } from "next/server";
// Can be whatever you want
const cookieName = "custom_user_id";
export function proxy(request) {
const response = NextResponse.next();
response.cookies.set(
cookieName,
request.cookies.get(cookieName)?.value ?? crypto.randomUUID(),
{
maxAge: 60 * 60 * 24 * 365,
path: "/",
secure: true,
sameSite: "lax"
}
);
return response;
}
export const config = {
// Without this, the cookie is sent with static assets too
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"]
};Server Setting the User ID From Cloudflare:
If you serve your storefront through Cloudflare, you can mint the cookie in a Worker on the same hostname as the document.
// Can be whatever you want
const cookieName = "custom_user_id";
export default {
async fetch(request) {
const response = await fetch(request);
// The cookie only survives ITP if it is set on the document response
if (!response.headers.get("content-type")?.includes("text/html")) {
return response;
}
const existingUserId = request.headers
.get("cookie")
?.split("; ")
.find(row => row.startsWith(`${cookieName}=`))
?.slice(cookieName.length + 1);
const newResponse = new Response(response.body, response);
newResponse.headers.append(
"Set-Cookie",
`${cookieName}=${existingUserId ?? crypto.randomUUID()}; Max-Age=31536000; Path=/; Secure; SameSite=Lax`
);
return newResponse;
}
};WARNING
Cloudflare strips Set-Cookie from responses that it caches, so on a cached route this fails silently and nobody receives a cookie. Send Cache-Control: private="set-cookie" on that response, or mint the cookie on an uncacheable path on the same hostname. Check the cf-cache-status response header before you rely on it.
Reading the Cookie in the Browser:
Once the cookie is set, read it in the browser and hand it to Elevar:
// Can be whatever you want
const cookieName = "custom_user_id";
const userId = document.cookie
.split("; ")
.find(row => row.startsWith(`${cookieName}=`))
?.slice(cookieName.length + 1);
if (userId) {
window.ElevarUserIdFn = () => userId;
}WARNING
Only define window.ElevarUserIdFn if you have a value to return. Returning an empty string - or anything that is not a string - logs USERID_FN_BAD_RETURN, and the user ID is instead generated using our built-in logic.
Shopify Hydrogen Implementation
Only for Shopify Headless integrations
For Hydrogen headless customers, you can retrieve the Client ID provided by Shopify using getClientBrowserParameters():
import { getClientBrowserParameters } from "@shopify/hydrogen";
window.ElevarUserIdFn = () => getClientBrowserParameters().uniqueToken;Important Notes
- Testing requires clean state: If you've run the Elevar script before defining your user ID function, clear all cookies and local storage to test properly.
- No impact on existing users: For customers already using Elevar's standard user IDs, implementing a custom user ID won't cause an increase in new user traffic. If a user's previous ID exists in local storage or cookies, Elevar will continue using it.