JavaScript (npm)
@caravela-studio/glancy-web is Hey Glancy for any browser app: React, Vue, Svelte, Angular, or plain TypeScript. It
sends events, page views, session replays and heatmaps, batches them, and flushes the queue when the tab is hidden or
closed so nothing is lost. It's fully typed.
On Next.js, use the Next.js SDK instead (it wraps this one). For a site without a build step, use the script tag.
Install
npm install @caravela-studio/glancy-web
Set up
Create one client and share it across your app:
// analytics.ts
import { BrowserClient } from "@caravela-studio/glancy-web";
export const glancy = new BrowserClient({
writeKey: "YOUR_WRITE_KEY",
host: "https://ingest.heyglancy.com",
});
The write key can only add events to one bucket, never read them, so it's safe to ship in your bundle. To keep it
out of your code, read it from your build tool's public environment variables (for example
import.meta.env.VITE_GLANCY_WRITE_KEY with Vite).
Page views
Call page() on every route change, from your router's navigation hook:
// React Router
const location = useLocation();
useEffect(() => { glancy.page(); }, [location.pathname]);
// Vue Router
router.afterEach(() => glancy.page());
Each page view is stored as Page Viewed with the path, URL, title, referrer and UTM parameters.
Events and users
glancy.track("Order Completed", { revenue: 42, currency: "EUR" });
// after sign up or sign in
glancy.identify(user.id, { name: user.name, email: user.email, plan: user.plan });
// on sign out
glancy.reset();
See Events, users and sessions for naming tips and how identities are merged.
Options
| Option | Default | |
|---|---|---|
writeKey, host |
Required | |
heatmaps |
false |
Collect clicks and scroll depth |
sessionReplay |
false |
true, or { sampleRate, maskAllInputs, maskAllText, maskTextSelector, blockSelector }; see Session replay |
redactParams |
key, token, code, password… |
URL query parameters replaced by redacted |
flushAt / flushInterval |
20 / 2000 ms |
Send a batch after this many events, or this long |
manualStart |
false |
Don't start replay and heatmaps until startRecording() |
debug |
false |
Log every event to the console |
export const glancy = new BrowserClient({
writeKey: "YOUR_WRITE_KEY",
host: "https://ingest.heyglancy.com",
heatmaps: true,
sessionReplay: { sampleRate: 0.5 },
});
Methods
| Method | What it does |
|---|---|
track(event, properties?) |
Sends an event |
page(name?, properties?) |
Sends a page view |
identify(userId, traits?) |
Ties this browser to a user and updates their profile |
group(groupId, traits?) |
Ties the user to a company or team |
alias(userId, previousId?) |
Merges an old id into a new one |
reset() |
Forgets the user; new anonymous id and session |
flush() |
Sends the queue now |
getAnonymousId(), getUserId(), getSessionId() |
The current ids |
startRecording(), stopRecording() |
Start or stop replay and heatmaps, for example on consent changes |
consent({ ads }) |
The visitor's ad consent from your banner; with ads: false their events are never sent to ad platforms (Ad conversions) |
Waiting for consent
If you need consent before recording sessions, configure replay and heatmaps with manualStart: true. Events are
sent as usual, and nothing is recorded until you call startRecording():
export const glancy = new BrowserClient({
writeKey: "YOUR_WRITE_KEY",
host: "https://ingest.heyglancy.com",
sessionReplay: true,
heatmaps: true,
manualStart: true,
});
onConsentGiven(() => glancy.startRecording());
onConsentWithdrawn(() => glancy.stopRecording());
On your server
@caravela-studio/glancy-web/server sends events from any JavaScript server (Node, Bun, Deno, edge runtimes). It
reads GLANCY_HOST and GLANCY_WRITE_KEY. Using Next.js? @caravela-studio/glancy-next/server has the same calls.
import { glancy, getAnonymousId } from "@caravela-studio/glancy-web/server";
await glancy().track({ userId: user.id, anonymousId: getAnonymousId(request.headers), event: "Order Completed", properties: { revenue: 42 } });
To record your API's requests with their status and duration_ms (filter by status ≥ 500 to find server errors),
send an API Request event per request:
- Fetch-style handlers (Hono, Remix, SvelteKit, Bun, Deno, Cloudflare Workers): wrap the handler with
withGlancyRoute(handler, { waitUntil }).waitUntil(e.g.ctx.waitUntilon Workers) sends in the background; without it the response waits for the send. A handler that throws counts as500and the error goes on. - Express / Connect:
app.use("/api", trackRequests()). It sends once the response is finished.
import express from "express";
import { trackRequests } from "@caravela-studio/glancy-web/server";
const app = express();
app.use("/api", trackRequests({ userId: (req) => req.headers["x-user-id"] }));
Both take event (default API Request), userId, writeKey, host and onError.