Quickstart
Send your first event to Pug in under five minutes. By the end you’ll have the Web SDK installed, a page_view flowing into your project, and confirmation in the dashboard.
Prerequisites
- A Pug account — sign up at app.pug.sh (or your self-hosted dashboard)
- Node.js 18+ and a bundler-based web project (Vite, Next.js, etc.)
Building a mobile app? This quickstart is web-only — start with the Flutter setup instead.
Building a backend? Use the server-side Node SDK, or POST events directly from any language — see Integrate over HTTP.
1. Create a project and grab your keys
- Sign in to the dashboard .
- Create an organization, then a project inside it.
- Collect the two values this quickstart needs:
- Project ID — in the dashboard URL:
/p/<projectId>. - Public key (
pub_…) — under the API Keys section of your project Overview; safe to ship in client code.
- Project ID — in the dashboard URL:
The Web SDK authenticates with your public key. (The same screen also shows a server-side private key — you don’t need it here; see Authentication.)
2. Install the Web SDK
npm install @pug-sh/browserSee Installation for pnpm, yarn, bun, and framework-specific setup. No bundler? Drop in the CDN loader snippet instead.
3. Initialize and track
Call init once at startup, then track anywhere. init registers your project ID and public key, starts the session, and begins auto-tracking (page views, clicks). The first argument to track is the event name — any string you choose.
Vite / React
// src/main.tsx
import { init, track } from '@pug-sh/browser'
init('YOUR_PROJECT_ID', {
apiKey: 'pub_YOUR_PUBLIC_KEY'
})
track('signup', { plan: 'pro' })Next.js App Router
init must run in the browser, so wrap it in a client component:
// components/PugProvider.tsx
'use client'
import { init } from '@pug-sh/browser'
import { useEffect } from 'react'
export function PugProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
init(process.env.NEXT_PUBLIC_PUG_PROJECT_ID!, {
apiKey: process.env.NEXT_PUBLIC_PUG_PUBLIC_KEY!
})
}, [])
return <>{children}</>
}Add PugProvider to your root layout and store the values in .env.local:
NEXT_PUBLIC_PUG_PROJECT_ID=your-project-id
NEXT_PUBLIC_PUG_PUBLIC_KEY=pub_your_public_keyOnly ever expose the public key with
NEXT_PUBLIC_*. The private key stays server-side.
4. Verify in the dashboard
- Open your project in the dashboard.
- Go to Live — the active visitor count should tick up within a few seconds.
- Go to Events —
page_view(auto-tracked on init) and yoursignupevent should appear.
Events are batched client-side and flushed every few seconds, so allow up to ~10 seconds for the first one. Force an immediate send with track('signup', {}, { immediate: true }).
5. Identify a user (optional)
After sign-in, link anonymous activity to a known user. identify() returns a promise and never throws — failures are logged — so you can await it without a try/catch:
import { identify } from '@pug-sh/browser'
await identify('user-123', { email: '[email protected]', plan: 'pro' })On sign-out, call reset() to start a fresh anonymous session. See Identity & sessions.
You’re done when…
-
page_viewandsignupappear in Live and Events - Auto-tracked clicks/scrolls appear (defaults are on)
-
identify()merges pre-login events into a profile (optional)
Common issues
| Symptom | Likely cause | Fix |
|---|---|---|
| No events in dashboard | Wrong project ID or public key | Re-check the API Keys section of your project Overview |
| Events in console but not dashboard | dryRun: true in init options | Remove dryRun or set false |
| CORS errors in the browser | Wrong endpoint | Use https://api.pugs.dev or your self-hosted URL |
| Nothing happens, no errors | init() ran outside the browser (SSR) | Call init() in a client component / useEffect |
| Events delayed | Client-side batching | Normal (see step 4) — or use { immediate: true } |
Next steps
Next: Core concepts — orgs, projects, keys, and the event pipeline. Then:
- Initialization — batching, sessions, auto-track options
- Tracking events — custom events and well-known schemas
- Authentication — public vs private keys