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

# Anonymous User Tracking and Pre-Login Identity Merging

> Capture the full user journey before login with anonymous IDs, then merge pre-signup events automatically when you call identify().

The Tappd Web SDK tracks every visitor from their very first page view — even before they create an account or log in. Each anonymous visitor gets a stable identifier stored in their browser, and the moment they identify themselves, all of their previous activity is automatically merged into their user profile.

## How Anonymous Tracking Works

<Steps>
  <Step title="First Visit — Anonymous ID Generated">
    When the SDK initializes for the first time, it generates a unique `anonymousId` (for example, `anon_xyz789abc123`) and stores it in `localStorage` under the key `_tappd_anonymous_id`. All events from this point forward are tracked against that ID.

    ```javascript theme={null}
    const tappd = new TappdSDK({ appId: 'YOUR_APP_ID' });

    // Anonymous ID is created automatically
    const anonymousId = tappd.getAnonymousId();
    console.log(anonymousId);
    // Output: anon_abc123def456ghi789
    ```
  </Step>

  <Step title="Anonymous Events Are Captured">
    Every `track()` and `trackPageView()` call is recorded with the `anonymousId`. You don't need to do anything special — it all happens behind the scenes.

    ```javascript theme={null}
    // All of these are captured and attributed to the anonymousId
    await tappd.track('page_view');
    await tappd.track('button_click', { buttonId: 'cta' });
    await tappd.track('add_to_cart', { productId: 'prod_123' });
    ```
  </Step>

  <Step title="User Identifies — Events Are Merged">
    When the user logs in or signs up, call `identify()`. The SDK automatically links all previous anonymous events to the newly identified user.

    ```javascript theme={null}
    await tappd.identify({
      external_id: 'user_123',   // Required: your internal user ID
      email: 'john@example.com',
      name: 'John Doe'
    });

    // All previous anonymous events are now merged with user_123
    ```
  </Step>

  <Step title="Future Events Use the Identified User">
    After `identify()` is called, events are tracked with the identified user's `external_id`. The `anonymousId` is retained as an alias for future matching.
  </Step>
</Steps>

## Anonymous ID Persistence

The anonymous ID is designed to survive normal browser activity, but it resets when users explicitly clear their data.

<CardGroup cols={2}>
  <Card title="Persists across..." icon="check" href="/web-sdk/anonymous-tracking#anonymous-id-persistence">
    * Browser sessions
    * Tab refreshes
    * Browser restarts
    * Device reboots (same browser)
  </Card>

  <Card title="Resets when..." icon="xmark" href="/web-sdk/anonymous-tracking#anonymous-id-persistence">
    * User clears browser data
    * User closes an incognito/private window
    * You call `tappd.reset()`
  </Card>
</CardGroup>

You can inspect the stored value directly:

```javascript theme={null}
// The anonymous ID is stored in localStorage
localStorage.getItem('_tappd_anonymous_id');
// Returns: anon_abc123def456...
```

## Use Cases

<Tabs>
  <Tab title="E-commerce">
    Capture product browsing and cart activity before a user creates an account, then attribute that behavior to the new user automatically.

    ```javascript theme={null}
    // Anonymous user browses and adds to cart
    await tappd.track('product_viewed', { productId: 'prod_123' });
    await tappd.track('add_to_cart', { productId: 'prod_123' });

    // User creates an account at checkout
    await tappd.identify({
      external_id: 'user_123',
      email: 'john@example.com',
      name: 'John Doe'
    });

    // Cart and browsing history are now linked to user_123
    ```
  </Tab>

  <Tab title="SaaS">
    Track pricing page visits and trial button clicks before signup so you can see the complete acquisition funnel from first touch to conversion.

    ```javascript theme={null}
    // Anonymous user visits the pricing page
    await tappd.track('page_view', { page: '/pricing' });
    await tappd.track('button_click', { buttonId: 'start-trial' });

    // User signs up for a trial
    await tappd.identify({
      external_id: 'user_123',
      email: 'john@example.com'
    });

    // You can now see the full journey: pricing page → trial signup
    ```
  </Tab>
</Tabs>

## Getting the Anonymous ID

```javascript theme={null}
const anonymousId = tappd.getAnonymousId();
console.log('Anonymous ID:', anonymousId);
```

## Resetting Anonymous Tracking

Call `tappd.reset()` when a user logs out. This clears the current identity, regenerates a fresh `anonymousId`, and ensures the next user on the same browser starts with a clean slate.

```javascript theme={null}
function handleLogout() {
  tappd.reset();
  // A new anonymousId is generated automatically
  // The user is now anonymous again
}
```

<Warning>
  Avoid calling `reset()` unless the user is explicitly logging out. Unnecessary resets break the anonymous-to-identified journey and prevent historical data from merging correctly.
</Warning>

## Data Privacy

Anonymous tracking is built with privacy compliance in mind:

* No personal information is stored in the `anonymousId`
* Fully GDPR compliant
* Users can opt out via standard browser privacy settings
* Anonymous data can be deleted on request

## Troubleshooting

<Accordion title="Anonymous ID changes on every page load">
  **Cause:** The browser has `localStorage` disabled or blocked.

  **Fix:** Check for `localStorage` support before initializing the SDK:

  ```javascript theme={null}
  if (typeof Storage !== 'undefined') {
    console.log('localStorage is supported — anonymous tracking will persist');
  } else {
    console.log('localStorage is not supported — anonymous IDs will reset on each load');
  }
  ```
</Accordion>

<Accordion title="Anonymous events are not appearing after identification">
  **Cause:** `identify()` was called without a valid `external_id`, or the anonymous context was lost before identification.

  **Fix:** Always pass a non-empty `external_id` when calling `identify()`. The SDK uses this to link the anonymous session to the user record server-side.

  ```javascript theme={null}
  // Correct — SDK automatically includes the anonymousId internally
  await tappd.identify({
    external_id: 'user_123',  // Required: must be a valid, non-empty string
    email: 'john@example.com'
  });
  ```
</Accordion>
