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

# Global JS

> One JavaScript block that runs on every page of the site, before any variant. For shared helpers, polyfills, or one-time analytics wiring.

**Site → Settings → Global JS.** Publish-gated (needs the Global JS
permission on this site, which requires Publish).

<Frame caption="Global JS panel. One editor, byte counter, publish-gated.">
  <img src="https://mintcdn.com/abtestly/TJFfWsOMn4uzK6iy/images/settings-global-js.png?fit=max&auto=format&n=TJFfWsOMn4uzK6iy&q=85&s=c97ff570ed769618bd0dbd89e31a8428" alt="Settings: Global JS" width="2400" height="1141" data-path="images/settings-global-js.png" />
</Frame>

## What it is

A single JavaScript block that runs once per page per visitor,
**before** any variant's code. Useful for:

* **Shared helpers.** A `waitFor` polyfill, a debounce utility, a
  branded logger. Instead of copying the same function into every
  variant, put it here.
* **One-time analytics wiring.** A GA4 `configure` call, a Segment
  identify, something your variants can then use without repeating.
* **Feature flags read from your own system.** Cache your feature flag
  values into a global so your variant JS can read them
  synchronously.

## When it runs

1. Consent gate has passed (see [Consent](/install/consent)).
2. Visitor is not opted out (see [Opt-out](/install/opt-out)).
3. **Global JS runs.** Once per page. Wrapped in a `try / catch`.
4. The rest of the runtime decides variants and applies them.

Because it runs before any variant, code in Global JS can define
utilities that variant code then uses.

## Once per page per site

`window.__abtestlyGlobalJsRan[siteId]` is set on first run. A second
call, for example on a SPA route change, is a no-op. The block runs
once and stays.

That is deliberate: shared helpers are almost always things you want
to define once. Anything that needs to fire on route change belongs in
[SPA panel](/settings/spa-panel), not here.

## Size cap

**50 KiB UTF-8**. The editor counts bytes and refuses to save over the
cap. Almost every real Global JS is well under this, even a large
utility library gzip-inlined into base64 has room.

## Errors

Errors thrown inside Global JS are caught, logged to the browser
console with a prefixed message, and reported to ABTestly via the
`/err` beacon (first-occurrence-per-session, deduped).

An error in Global JS does **not** stop variant JS from running. It
does mean whatever your Global JS was supposed to provide is not
available to variants; if variants blindly assume it worked, they will
throw too.

Wrap risky code in try/catch inside Global JS and fall back gracefully.

## Example

```js theme={null}
// site-wide helper: a small debounce
window.__ab = window.__ab || {};
window.__ab.debounce = function (fn, ms) {
  let t;
  return function () {
    clearTimeout(t);
    t = setTimeout(() => fn.apply(this, arguments), ms);
  };
};

// site-wide config
window.__ab.cfg = {
  brandName: 'Acme',
  isPro: /pro=1/.test(location.search),
};
```

Variants can then:

```js theme={null}
window.__ab.debounce(track, 200)();
if (window.__ab.cfg.isPro) { … }
```

## What Global JS is *not* for

* **Variant code.** If it belongs to a specific experiment, put it in
  the variant. Global JS is site-wide, always-on. A "test" living in
  Global JS is not a test.
* **Anything with heavy DOM manipulation.** DOM changes belong in
  variants where they can be scoped and cleaned up on SPA
  route change.
* **Third-party script loading.** Load third parties from your own
  `<script>` tags in `<head>` or from a tag manager. Adding them
  through Global JS makes it hard to reason about their load order
  relative to the rest of the page.

## Redacted from the audit log

The full text of Global JS is redacted in the activity log. Only the
event of the change and who made it is visible; the actual code is
not. This is because Global JS commonly contains site-specific keys or
identifiers you would not want in an audit-log export.
