Identify signed-in users
Sessions are anonymous (visitor a8f2k…) until your app says who is signed in. One call attaches the user to the whole visit — even retroactively within the session — and makes sessions searchable by person. This requires your own login system; static sites can skip it.
The call
// after your login succeeds:
logcohort('identify', {
userId: user.id, // required — your stable id for this user
email: user.email, // optional
name: user.name, // optional
});Call it any time after your login succeeds. The command queue buffers it until the recorder has loaded, so there is no race with the script tag — you can never call it "too early".
Next.js (App Router)
When the user is known on the server (a session cookie), the clean pattern is: pass the user down as props from a server component, and call the queue in a tiny client component. No string-building, no escaping — React passes the values:
// app/logcohort-identify.tsx
"use client";
import { useEffect } from "react";
export function LogcohortIdentify({ userId, email, name }: {
userId: string;
email?: string | null;
name?: string | null;
}) {
useEffect(() => {
window.logcohort?.("identify", { userId, email, name });
}, [userId, email, name]);
return null;
}// app/layout.tsx — pass the server session down as props:
const session = await getServerSession();
<body>
{children}
{session?.user && (
<LogcohortIdentify
userId={session.user.id}
email={session.user.email}
name={session.user.name}
/>
)}
</body>This is race-free by construction: the loader stub in your layout runs during HTML parse, so window.logcohort exists before any effect fires, and the queue holds the identify until the recorder is ready. It's the exact pattern the logcohort dashboard uses to record itself.
What gets stored: only what you pass — an id, and optionally email and name. Identify never reads anything from the page on its own.