Astro

Install Clamp in an Astro site. Auto-pageviews via inline script, server-side events from API routes, queryable by your AI agent via MCP.

Install

terminal
npm install @clamp-sh/analytics

Setup

Add an inline <script> tag to your root layout. Astro bundles and deduplicates inline scripts automatically.

src/layouts/Layout.astro
---
// your existing frontmatter
---
<html>
  <body>
    <slot />

    <script>
      import { init } from "@clamp-sh/analytics"
      init("proj_xxx")
    </script>
  </body>
</html>

Pageviews — including client-side navigations via <ViewTransitions /> — are tracked automatically.

Track custom events

Import track from the main SDK entry in any Astro component script:

src/components/SignupButton.astro
<button id="signup-btn">Sign up</button>

<script>
  import { track } from "@clamp-sh/analytics"

  document.getElementById("signup-btn")?.addEventListener("click", () => {
    track("signup_started", { plan: "pro" })
  })
</script>

Properties must be flat string key-value pairs. See Events for limits and schema.

Server-side events

From an Astro API route, use the /server entry with an API key:

src/pages/api/signup.ts
import type { APIRoute } from "astro"
import { init, track } from "@clamp-sh/analytics/server"

init({ projectId: "proj_xxx", apiKey: import.meta.env.CLAMP_API_KEY })

export const POST: APIRoute = async ({ request }) => {
  const { anonymousId } = await request.json()

  await track("signup_completed", {
    anonymousId,
    properties: { plan: "pro" },
  })

  return new Response(JSON.stringify({ ok: true }))
}

Pass anonymousId from the browser (via getAnonymousId()) to link server events to the visitor session.

Verify

  1. Deploy or open your site at a non-localhost URL (localhost is skipped by design).
  2. Navigate between a few pages.
  3. In your Clamp dashboard, pageviews should appear within ~5 seconds.
  4. DevTools → Network → filter for e/batch to confirm requests are sending.

Gotcha

Use an inline <script>tag — not the component’s server-side script block (the --- fence). Code inside the fence runs on the server and has no access to window, so init() will throw. If you prefer the React component, add the client:load directive: <Analytics client:load projectId="proj_xxx" />.

Next step