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.
loadFeatureFlagsbooleanEvaluate feature flags on init. Default true.
sendExposureEventsbooleanTrack $feature_flag_called on flag reads. Default true.
flagPersistencestring"networkOnly", "networkFirst", or "persistenceUntilNetworkSuccess" (default). See Feature flags.
flagCacheTtlMsnumberHow long the persisted flag cache stays valid, checked when the cache is adopted - adopted values keep serving until the next reload or identity change. Default 86400000 (24 h).

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();

Feature flags

Flags are evaluated on init by default and cached per identity (localStorage, scoped by distinct_id, bounded by flagCacheTtlMs). The cache serves immediately on the next launch until the first successful reload replaces it; flagPersistence controls that policy - under the default "persistenceUntilNetworkSuccess" an expired cache still serves up to 7 days stale, while "networkFirst" serves only inside the TTL.

ts
const value = cohorly.getFeatureFlag("checkout-redesign", "control");
const enabled = cohorly.isFeatureEnabled("checkout-redesign", false);
const details = cohorly.getFeatureFlagDetails("checkout-redesign");
// details.source: "network" | "persistence" | "fallback"

A read that resolves to the fallback is a default, not an exposure. See Feature flags for the persistence policies, TTL, and source semantics.

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