SDK

A lightweight analytics SDK for Clamp. Auto-instrumented, privacy-focused, under 1KB gzipped.

Install

terminal
npm install @clamp-sh/analytics

Browser

app.ts
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

app/layout.tsx
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

server.ts
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:

HTML
<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.

types.ts
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 error

Works 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.

Browser
const anonId = getAnonymousId()
await fetch("/api/checkout", {
  method: "POST",
  body: JSON.stringify({ plan: "pro", anonId }),
})
Server
await track("checkout_completed", {
  anonymousId: req.body.anonId,
  properties: { plan: "pro", amount: "49" },
})

Framework examples

Next.js App Router

app/layout.tsx
import { Analytics } from "@clamp-sh/analytics/react"

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics projectId="proj_xxx" />
      </body>
    </html>
  )
}
app/pricing/page.tsx (client component)
"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

app/actions.ts
"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

server.ts
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

ConstraintLimit
Max keys20
Key length128 chars
Value length512 chars
Payload size64 KB

Properties are flat string → string maps. Nested objects, arrays, and non-string values are rejected.