Web SDK

@cohorly/web is the browser SDK. It is built on the shared core with localStorage persistence, a fetch/sendBeacon transport (so events survive tab close and navigation), and optional SPA pageview autotracking.

Install

bash
pnpm add @cohorly/web
# or: npm install @cohorly/web

Initialize

Call init() once at your app entry point. Pass your project token so events route to the right project; apiHost defaults to the hosted Cohorly API.

ts
import { init } from "@cohorly/web";
const cohorly = init({
token: "YOUR_PROJECT_TOKEN",
trackPageviews: true,
});

After init() has run once, you can import the named cohorly singleton anywhere instead of threading the returned instance through your app:

ts
import { cohorly } from "@cohorly/web";
cohorly.track("Button Clicked", { button: "checkout" });

init() is SSR-safe: browser-only APIs (localStorage, window, sendBeacon) fall back to in-memory/no-op implementations when unavailable.

Options

OptionTypeNotes
apiHoststringOptional. Ingestion API base URL. Defaults to the hosted Cohorly endpoint.
tokenstringProject token stamped on every event and request.
trackPageviewsbooleanAutotrack SPA pageviews.
flushIntervalMsnumberHow often the queue auto-flushes.
batchSizenumberFlush once this many events are queued.
superPropertiesobjectProperties merged into every event from the start.
debugbooleanVerbose logging.

Track events

track(event, properties?) queues an event. The SDK automatically merges in $browser, $os, $current_url, and screen size, plus distinct_id, time, $insert_id, and $lib.

ts
cohorly.track("Signed Up", { plan: "pro" });
cohorly.track("Item Added", { sku: "A-100", price: 29 });

Identify

identify(id) switches the current user to a known id. The switch (distinct id, persisted state) is applied synchronously, before any network call, so a slow or offline request never delays it. On the first call after being anonymous it then sends an /alias request so pre-login and post-login activity link into one profile; the request is best effort - a failure neither throws nor blocks, since the server also links the anonymous and identified ids implicitly from the next event that carries both (see Identity).

ts
await cohorly.identify("user_123");

Use reset() on logout to generate a fresh anonymous id. This matters on shared devices: without it, the next person to use the device inherits the previous person's anonymous history.

ts
cohorly.reset();

Super properties

Super properties are merged into every subsequent event. Register and unregister them with register() / unregister(). Event-level properties override super properties of the same key.

ts
cohorly.register({ app_version: "1.2.3", plan: "free" });
cohorly.track("Viewed Page"); // includes app_version and plan
cohorly.unregister("plan");

People profiles

Update the current user's profile through cohorly.people. These map to the /engage endpoint.

ts
await cohorly.people.set({ name: "Ada Lovelace", plan: "pro" });
await cohorly.people.setOnce({ first_seen: Date.now() });
await cohorly.people.increment({ logins: 1 });
await cohorly.people.unset(["temp_flag"]);
await cohorly.people.delete();

Flush and accessors

ts
await cohorly.flush(); // send the queued batch now
const id = cohorly.getDistinctId();
const anon = cohorly.isAnonymous();

The queue is persisted to localStorage, so events survive reloads and are retried on the next flush if a request fails.

Keeping the token out of the bundle

Passing token to init() embeds it in your client JS. If you would rather not, proxy ingestion server-side with @cohorly/nextjs's createCohorlyProxy() and omit token here. See the Next.js SDK.

PreviousCore
NextReact