Get started

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.

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 or review the Troubleshooting Guide.