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

# Pre-init Queue

> How Zenstep's call queue works — safe to call identify() and track() before the snippet has loaded.

## Overview

Zenstep uses a queue pattern (similar to the GA4 dataLayer) to handle calls made before the snippet bundle has evaluated. This means you can safely call `window.zenstep.identify()` and `window.zenstep.track()` anywhere in your page — even in `<head>` scripts that run before `<body>`.

***

## How it works

When the snippet's `<script>` tag is added to the page, the browser begins loading `snippet.js` asynchronously. In the meantime, the snippet installs a lightweight stub on `window.zenstep`:

```javascript theme={null}
// This stub is installed synchronously by the snippet script tag
window.zenstep = {
  identify: (...args) => {
    _queue.push(["identify", args]);
  },
  track: (...args) => {
    _queue.push(["track", args]);
  },
  _q: [],
};
```

When `snippet.js` finishes loading, it:

1. Reads `window.zenstep._q`
2. Replays each queued call with the real implementation
3. Replaces the stub with the full API

***

## Why this matters

You do not need to wait for the snippet to load before calling `identify()`. Call it wherever the user data is available — in a layout component, on DOMContentLoaded, or even inline in `<head>`:

```html theme={null}
<head>
  <script>
    // Runs before the snippet loads — safe
    window.zenstep = window.zenstep || { _q: [] };
    window.zenstep.identify =
      window.zenstep.identify ||
      function (...a) {
        window.zenstep._q.push(["identify", a]);
      };
    window.zenstep.identify("user_123", { plan: "grow" });
  </script>
</head>
<body>
  ...
  <script
    async
    src="https://cdn.getzenstep.com/v1/snippet.js"
    data-zenstep="KEY"
  ></script>
</body>
```

***

## Queue inspection

You can inspect the queue at any time via `window.zenstep._q`. Each entry is a tuple of `[methodName, argsArray]`:

```javascript theme={null}
console.log(window.zenstep._q);
// [["identify", ["user_123", { plan: "grow" }]], ["track", ["feature_used"]]]
```

After the snippet initialises, `_q` is an empty array (all calls have been replayed).

***

## Order guarantee

Calls are replayed in FIFO order. If you call `identify()` before `track()`, the queue processes them in that order. This matters because `track()` uses the identity set by the most recent `identify()` call.

***

## Edge cases

**Snippet fails to load** — if the CDN is unreachable, `snippet.js` never evaluates. Calls remain in `_q` but are never replayed. Your page continues to function normally — Zenstep fails silently.

**Multiple `identify()` calls** — if you call `identify()` twice before the snippet loads, both are queued. The second call merges attributes into the first (Zenstep does not replace the full identity object on re-identification).
