# Installation

Add Totallytics to your application layout, single-page app, or modern frontend framework.

## Script placement

Load `latest.js` once in your root document. Do not mount duplicate script tags across child views.

### Plain HTML

Place the script in your shared template header or footer:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>My App</title>
    <script
      async
      src="https://analytics.bitgate.dev/latest.js"
      data-hostname="example.com"
    ></script>
  </head>
  <body>
    <main></main>
  </body>
</html>
```

### React and Vite

Add the script directly inside `index.html` at the project root:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
    <script
      async
      src="https://analytics.bitgate.dev/latest.js"
      data-hostname="example.com"
    ></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
```

### Next.js (App Router)

Place a raw `<script>` tag inside `app/layout.tsx` within the `<body>` element. Totallytics does not require an external npm package:

```tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <script
          async
          src="https://analytics.bitgate.dev/latest.js"
          data-hostname="example.com"
        />
      </body>
    </html>
  );
}
```

The tag is included in the rendered document; tracking runs in the visitor’s browser. For strict CSP, follow your framework’s nonce or hash setup for any inline code.

## Single-page navigation

`latest.js` automatically hooks the browser `window.history.pushState` API and listens to `popstate` events. These navigation events trigger pageviews when the tracked path changes. Consecutive identical paths are suppressed; `replaceState` alone does not trigger a pageview.

Do not add a second manual pageview for a route already covered by automatic tracking.

If your frontend uses hash-based routing (such as `example.com/#/dashboard`), add the `data-mode="hash"` attribute to track hash fragment transitions:

```html
<script
  async
  src="https://analytics.bitgate.dev/latest.js"
  data-hostname="example.com"
  data-mode="hash"
></script>
```

## Manual pageview tracking

If you prefer manual control over pageviews, disable automatic collection with `data-auto-collect="false"`. When disabled, trigger pageviews using `window.sa_pageview(path, metadata)`.

Calls recording an identical consecutive path are automatically suppressed. Verify that the script has loaded before calling `window.sa_pageview`:

```html
<script>
  function trackInitialPage() {
    if (typeof window.sa_pageview !== "function") {
      console.warn(
        "Pageview tracking is unavailable; check DNT and script loading.",
      );
      return;
    }

    window.sa_pageview(window.location.pathname);
  }
</script>
<script
  async
  src="https://analytics.bitgate.dev/latest.js"
  data-hostname="example.com"
  data-auto-collect="false"
  onload="trackInitialPage()"
></script>
```

## Script configuration options

Configure behavior by setting data attributes on the `<script>` tag:

- `data-hostname`: The registered site hostname (e.g., `example.com`).
- `data-ignore-pages`: Comma-separated path patterns with wildcards to ignore (e.g., `/admin/*,/checkout/receipt`).
- `data-allow-params`: Extra comma-separated query keys to keep, beyond the campaign parameters the script already recognizes (e.g., `plan`). Keep values non-sensitive.
- `data-mode`: Set to `"hash"` for hash-based client routers.
- `data-auto-collect`: Set to `"false"` to disable automated pageviews on load and history navigation.
- `data-strict-utm`: Set to `"true"` to keep automatic campaign-parameter collection to `utm_` keys rather than short aliases such as `source` and `campaign`.

You can also assign settings via `window.sa_settings` before the script loads:

```html
<script>
  window.sa_settings = {
    hostname: "example.com",
    autoCollect: false,
  };
</script>
<script async src="https://analytics.bitgate.dev/latest.js"></script>
```

For most settings, `window.sa_settings` overrides the equivalent attribute; `data-auto-collect="false"` still disables automatic pageviews. Avoid conflicting configurations and set them before `latest.js` executes. Totallytics does not host the optional [automatic-events companion scripts](/docs/events#automated-event-scripts).

## Privacy and Do Not Track

By default, `navigator.doNotTrack === "1"` stops normal collection after the script loads. Keep this default to respect the visitor’s setting.

## Content Security Policy (CSP)

Totallytics uses `new Image()` for pageview beacons and `navigator.sendBeacon` for `/append` duration tracking.

If your site serves a Content Security Policy header, merge `https://analytics.bitgate.dev` into your existing directives. Do not overwrite your wider security rules:

```text
script-src 'self' https://analytics.bitgate.dev;
img-src 'self' https://analytics.bitgate.dev;
connect-src 'self' https://analytics.bitgate.dev;
```

If your policy defines `script-src-elem`, include `https://analytics.bitgate.dev` there as well. Inline settings, event queues, and `onload` examples also need your existing inline-code policy; prefer external app code or your framework’s nonce/hash mechanism rather than adding `unsafe-inline`.

## Testing on localhost

By default, `latest.js` extracts `location.host` when `data-hostname` is omitted. On local development environments, this results in values like `localhost:3000`.

The script can still send on localhost; it is not a reliable development-mode exclusion. An unregistered local hostname is normally ignored by the collector, while an explicit production `data-hostname` can record local test traffic against your live site. To verify integrations, test on an explicit staging or production domain that matches a registered hostname. Do not direct localhost test traffic to your production hostname.

Next, explore [Custom Events](/docs/events) or review the [Troubleshooting Guide](/docs/troubleshooting).
