---
title: "Quickstart"
description: "Send your first event to Pug in under five minutes - install the Web SDK, track a page view, and confirm it in the dashboard."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.pug.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# 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](https://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](/sdks?platform=flutter) instead.

Building a **backend**? Use the server-side [Node SDK](/sdks?platform=node), or POST events directly from any language - see [Integrate over HTTP](/sdks?platform=http).

Working with a **coding agent**? [Install with AI](/get-started/ai-setup) has a
ready-to-paste prompt for each platform that points the agent at these docs.

## 1. Create a project and grab your keys

1. Sign in to the [dashboard](https://app.pug.sh).
2. Create an **organization**, then a **project** inside it.
3. 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.

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](/get-started/authentication).)

## 2. Install the Web SDK

```bash
npm install @pug-sh/browser
```

See [Installation](/sdks#install) for pnpm, yarn, bun, and framework-specific setup. No bundler? Drop in the [CDN loader snippet](/sdks#loader-snippet) instead.

## 3. Initialize and track

Call `init` once at startup, then `track` anywhere. `init` registers your project ID and public key and begins auto-tracking (page views, clicks). The first argument to `track` is the **event name** - any string you choose.

> **This install is cookieless until you ask for consent**
>
> With no `trackingConsent` set, the SDK captures events but writes **nothing** to
> the device (no anonymous ID, no session, no cookie) and the server derives a
> daily-rotating anonymous ID instead. Everything in this quickstart works that
> way; the one thing that doesn't is `identify()`, which needs a stored identifier
> to merge. See [Tracking consent](/sdks#tracking-consent) for the three states and
> how to gate them behind a banner.

### Vite / React

```ts
// 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:

```tsx
// 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_key
```

> Only ever expose the **public** key with `NEXT_PUBLIC_*`. The private key stays server-side.

## 4. Verify in the dashboard

1. Open your project in the dashboard.
2. Go to **Live** - the active visitor count should tick up within a few seconds.
3. Go to **Events** - `page_view` (auto-tracked on init) and your `signup` event 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. This step needs **granted** consent - there is no anonymous ID to merge while the default cookieless state is in force, so call `optInTracking()` once the user has agreed:

```ts
import { identify, optInTracking } from '@pug-sh/browser'

optInTracking()
await identify('user-123', { email: 'user@example.com', plan: 'pro' })
```

`identify()` returns a promise and **never throws** (invalid input, a missing identifier and transport failures are logged and the promise resolves), so you can `await` it without a try/catch. On sign-out, call `reset()` to start a fresh anonymous session. See [Identity & sessions](/sdks#identify-and-sessions).

## You're done when...

- [ ] `page_view` and `signup` appear in **Live** and **Events**
- [ ] Auto-tracked clicks/scrolls appear (defaults are on)
- [ ] `identify()` merges pre-login events into a profile (optional - needs granted consent)

## 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 }` |
| `identify()` does nothing, warns once | Consent is `cookieless` (the default) - no stored ID to merge | `optInTracking()` after the user agrees; see [Tracking consent](/sdks#tracking-consent) |
| Event counts look right, unique-user counts look low | Cookieless IDs rotate daily, so unique-user metrics, funnels and retention exclude them by default | Expected before consent; see [`include_cookieless`](/api/insights#cookieless-visitors-are-excluded-from-user-counts) |

## Next steps

**Next: [Core concepts](/get-started/concepts)** - orgs, projects, keys, and the event pipeline. Then:

- [Initialization](/sdks#initialize): batching, sessions, auto-track options
- [Tracking events](/sdks#track-events): custom events and well-known schemas
- [Authentication](/get-started/authentication): public vs private keys

Source: https://docs.pug.sh/get-started/index.mdx
