SDK
A lightweight analytics SDK for Clamp. Auto-instrumented, privacy-focused, under 1KB gzipped.
Install
npm install @clamp-sh/analyticsBrowser
import { init, track, getAnonymousId } from "@clamp-sh/analytics"
init("proj_xxx")
// Custom events
track("signup", { plan: "pro" })
// Get visitor ID (for linking server-side events)
const anonId = getAnonymousId()After init(), pageviews are tracked automatically, including SPA navigations. No additional setup needed.
React
import { Analytics } from "@clamp-sh/analytics/react"
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics projectId="proj_xxx" />
</body>
</html>
)
}Add to your root layout. Pageviews are tracked automatically. Use track() from @clamp-sh/analytics anywhere in your app for custom events.
Server
import { init, track } from "@clamp-sh/analytics/server"
init({ projectId: "proj_xxx", apiKey: "sk_proj_..." })
await track("account_created", {
anonymousId: "anon_abc123",
properties: { plan: "pro" },
})Server events require an API key from your project settings. The anonymousId links server events to a browser visitor.
Script tag
If you're not using a bundler:
<script src="https://cdn.jsdelivr.net/npm/@clamp-sh/analytics@0.2.0/dist/cdn.global.js"></script>
<script>
clamp.init("proj_xxx")
</script>Custom events
Track any action with track(name, properties). Properties are flat string key-value pairs.
track("signup", { plan: "pro", source: "pricing_page" })
track("feature_used", { name: "csv_export" })
track("invite_sent", { role: "editor", team: "acme" })On the server, wrap properties in an object with anonymousId:
await track("subscription_created", {
anonymousId: "anon_abc123",
properties: { plan: "pro", interval: "monthly" },
})See Events for property limits and what counts as an event.
Typed events
Define your event map once and get autocomplete and type checking across your app. Zero runtime cost.
type Events = {
signup: { plan: string; source: string }
purchase: { amount: string; currency: string }
feature_used: { name: string }
invite_sent: { role: string }
}
init<Events>("proj_xxx")
track("signup", { plan: "pro", source: "homepage" }) // ✓ autocomplete
track("signup", { wrong: "field" }) // ✗ type error
track("unknown_event") // ✗ type errorWorks the same way with the server SDK:
import { init, track } from "@clamp-sh/analytics/server"
init<Events>({ projectId: "proj_xxx", apiKey: "sk_proj_..." })
await track("purchase", {
properties: { amount: "49", currency: "usd" },
})Linking browser and server events
Pass the anonymous ID from the browser to your API, then include it in server-side events to connect the two.
const anonId = getAnonymousId()
await fetch("/api/checkout", {
method: "POST",
body: JSON.stringify({ plan: "pro", anonId }),
})await track("checkout_completed", {
anonymousId: req.body.anonId,
properties: { plan: "pro", amount: "49" },
})Framework examples
Next.js App Router
import { Analytics } from "@clamp-sh/analytics/react"
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics projectId="proj_xxx" />
</body>
</html>
)
}"use client"
import { track } from "@clamp-sh/analytics"
export default function Pricing() {
return (
<button onClick={() => track("plan_selected", { plan: "growth" })}>
Choose Growth
</button>
)
}Next.js Server Actions
"use server"
import { track } from "@clamp-sh/analytics/server"
export async function createTeam(name: string, anonId: string) {
const team = await db.teams.create({ name })
await track("team_created", {
anonymousId: anonId,
properties: { team_id: team.id },
})
return team
}Express / Node.js
import express from "express"
import { init, track } from "@clamp-sh/analytics/server"
init({ projectId: "proj_xxx", apiKey: "sk_proj_..." })
const app = express()
app.post("/api/subscribe", async (req, res) => {
await track("subscription_started", {
anonymousId: req.body.anonId,
properties: { plan: req.body.plan },
})
res.json({ ok: true })
})Properties limits
| Constraint | Limit |
|---|---|
| Max keys | 20 |
| Key length | 128 chars |
| Value length | 512 chars |
| Payload size | 64 KB |
Properties are flat string → string maps. Nested objects, arrays, and non-string values are rejected.