# Web Analytics Script Installation



The VitalSentinel Web Analytics script provides privacy-focused web analytics with automatic e-commerce tracking. It's lightweight (\~13KB gzipped) and respects user privacy while still giving you the insights you need.

## Basic Installation [#basic-installation]

Add this script to the `<head>` section of your HTML:

```html
<script
  src="https://analytics.vitalsentinel.com/analytics.js"
  data-key="YOUR_TRACKING_ID"
  async
></script>
```

<Callout type="info">
  Find your tracking ID in **Domain Settings** → **Web Analytics**.
</Callout>

## Configuration Options [#configuration-options]

Customize the script behavior using `data-*` attributes:

| Attribute            | Type    | Default        | Description                                        |
| -------------------- | ------- | -------------- | -------------------------------------------------- |
| `data-key`           | string  | required       | Your unique tracking ID                            |
| `data-storage`       | string  | "none"         | Storage consent: "none", "session", "persistent"   |
| `data-cookie-domain` | string  | (current host) | Cookie scope for multi-property/subdomain setups   |
| `data-debug`         | boolean | false          | Enable console logging                             |
| `data-spa`           | string  | "false"        | SPA route-change tracking: "true", "false", "auto" |

### Storage Consent Levels Explained [#storage-consent-levels-explained]

The `data-storage` attribute (and the matching `grantConsent()` API) controls how the script remembers visitors. Pick the lowest level that meets your needs - lower levels collect less personal data and require less consent under GDPR.

Only `session` and `persistent` are recognized. A missing attribute, or any other value including a typo such as `persistant`, falls back to `none`.

| Level              | What it stores                                                                                                                                                                                                                                                          | Best for                                                                                                |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| **none** (default) | Nothing - no cookies, no localStorage, no sessionStorage. Multi-page visits are stitched into one session on our servers instead, so nothing is kept on the visitor's device. The result is pseudonymous, not anonymous.                                                | Privacy-first sites that want to avoid the cookie banner entirely                                       |
| **session**        | Session-only storage that's cleared when the browser tab closes. Lets the script connect multiple pageviews into a single session, but doesn't recognize returning visitors.                                                                                            | Sites that want session-based metrics (pages per session, session duration) without persistent tracking |
| **persistent**     | Long-lived storage that survives across browser sessions. Enables returning-visitor recognition and first-touch attribution, and is the only level at which device details (screen size, window size, pixel ratio, touch capability, connection quality) are collected. | Sites that need to recognize returning visitors and report on device and connection                     |

<Callout type="warn">
  `session` and `persistent` both store data on your visitor's device, so you need their consent before you enable either. `none` stores nothing, so it needs no consent banner for storage.
</Callout>

### Cookie Domain for Subdomains [#cookie-domain-for-subdomains]

If you serve content across multiple subdomains (e.g. `www.example.com` and `app.example.com`) and use `persistent` storage, set `data-cookie-domain="example.com"` so the same visitor is recognized across both. With the default scoping, each subdomain would track visitors separately.

```html
<script
  src="https://analytics.vitalsentinel.com/analytics.js"
  data-key="abc123"
  data-storage="persistent"
  data-cookie-domain="example.com"
  async
></script>
```

### Example with Options [#example-with-options]

```html
<script
  src="https://analytics.vitalsentinel.com/analytics.js"
  data-key="abc123"
  data-storage="session"
  data-debug="true"
  async
></script>
```

## What Data is Collected [#what-data-is-collected]

### Page Views [#page-views]

* URL and page title
* Referrer information
* Time on page
* Scroll depth

### Session Data [#session-data]

* Session ID (pseudonymous)
* Visitor ID (only with `persistent` storage)
* Pages per session
* Session duration

### Traffic Attribution [#traffic-attribution]

* UTM parameters (source, medium, campaign, term, content)
* Referrer domain classification
* Traffic source type, classified server-side (direct, search, social, referral, email, newsletter, paid, paid search, paid social, AI search, internal)
* First-touch attribution (requires `persistent` storage)

### User Behavior [#user-behavior]

* Scroll depth tracking (25%, 50%, 75%, 90%, 100%)
* Time on page (active and total)
* Rage click detection (3+ clicks in 1 second within 100px)
* Outbound link clicks
* File downloads
* Site search queries. Email addresses, JWTs, well-formed phone numbers and long digit runs are replaced before the term is stored, unless storage is set to `persistent`. Short product codes such as `ABC-12345` are kept intact. You can stop search terms and page titles being stored at all with the **Do not store page titles or site-search terms** checkbox in **Domain Settings** → **Web Analytics** → **Advanced settings**

### Device Information [#device-information]

These are read from the browser only when storage is set to `persistent`, or after your consent banner calls `grantConsent('persistent')`:

* Screen resolution and window dimensions
* Device pixel ratio
* Touch capability
* Connection type and network info

Language, country, browser, operating system and device type are worked out on our servers from the request headers every browser sends anyway, so they are available in every storage mode.

## E-commerce Auto-Tracking [#e-commerce-auto-tracking]

The Web Analytics script automatically detects and tracks e-commerce events on popular platforms.

### Supported Platforms [#supported-platforms]

* **Shopify**
* **WooCommerce**
* **Magento**
* **Squarespace**

### Events Tracked [#events-tracked]

| Event              | Description                  |
| ------------------ | ---------------------------- |
| `product_view`     | Visitor views a product page |
| `add_to_cart`      | Item added to cart           |
| `remove_from_cart` | Item removed from cart       |
| `view_cart`        | Cart page viewed             |
| `begin_checkout`   | Checkout started             |
| `purchase`         | Order completed              |

### Data Captured [#data-captured]

For each e-commerce event:

* Product ID and SKU
* Product name and category
* Price and quantity
* Cart total
* Order ID (for purchases)

On Shopify, the cart contents are read back from the visitor's own cart, which lives on their device, so they are only collected when `data-storage` is `session` or `persistent`. With the default `none`, `view_cart` and `begin_checkout` are still counted, but without items, quantities or totals, and the cart poll on `/cart` does not start. `product_view`, `add_to_cart` and `purchase` are unaffected. Magento, WooCommerce and Squarespace read the cart from the rendered page, so this setting does not change what they send.

<Callout type="info">
  E-commerce tracking is automatic. No additional configuration is needed for supported platforms.
</Callout>

## Platform-Specific Installation [#platform-specific-installation]

### WordPress [#wordpress]

Use a header scripts plugin, or add it to your theme's `functions.php`:

```php
function vitalsentinel_analytics_script() {
    ?>
    <script src="https://analytics.vitalsentinel.com/analytics.js" data-key="YOUR_TRACKING_ID" async></script>
    <?php
}
add_action('wp_head', 'vitalsentinel_analytics_script');
```

See [Platform Guides](/installation/platforms) for the `wp_enqueue_script` alternative.

### Shopify [#shopify]

1. Go to **Online Store** → **Themes**
2. Click &#x2A;*...** → **Edit code** on your active theme
3. Open `theme.liquid` in the Layout folder
4. Add the script before `</head>`

E-commerce tracking will work automatically.

### WooCommerce [#woocommerce]

Install the script in your theme. E-commerce events will be tracked automatically by detecting WooCommerce elements.

### Next.js [#nextjs]

In your root layout (`app/layout.tsx`):

```jsx
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Script
          src="https://analytics.vitalsentinel.com/analytics.js"
          data-key="YOUR_TRACKING_ID"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}
```

## SPA Support [#spa-support]

SPA (single-page app) route tracking is **opt-in**. By default (`data-spa="false"`) the script records only the initial page load. To track client-side route changes, set `data-spa="true"`.

### Enabling SPA Tracking [#enabling-spa-tracking]

```html
<script
  src="https://analytics.vitalsentinel.com/analytics.js"
  data-key="YOUR_TRACKING_ID"
  data-spa="true"
  async
></script>
```

With SPA tracking enabled, the script detects navigation via:

* History API (pushState, replaceState)
* Popstate events
* Hash change events
* Navigation API (modern browsers)

<Callout type="info">
  `data-spa="auto"` enables a legacy heuristic that tries to detect SPAs automatically. It is opt-in only, because it can misfire on server-rendered sites that ship framework-like markers.
</Callout>

### Manual Page View Tracking [#manual-page-view-tracking]

For complete control:

```javascript
window.VitalSentinelAnalytics.trackPageView({
  path: '/new-page',
  title: 'New Page Title'
});
```

## API Methods [#api-methods]

All methods are available via `window.VitalSentinelAnalytics` (or the shorter alias `window.VSAnalytics`).

### Track Custom Events [#track-custom-events]

```javascript
window.VitalSentinelAnalytics.track('signup', {
  plan: 'premium',
  source: 'homepage'
});
```

### Track Page Views [#track-page-views]

```javascript
window.VitalSentinelAnalytics.trackPageView({
  path: '/virtual-page',
  title: 'Virtual Page'
});
```

### Identify Users [#identify-users]

Person-level identification is not supported. `identify()` stays callable so existing code on your site keeps working, but it stores nothing, sends nothing and records nothing.

### Set Global Properties [#set-global-properties]

```javascript
// Added to all subsequent events
window.VitalSentinelAnalytics.setGlobalProperties({
  environment: 'production',
  appVersion: '2.1.0'
});
```

### Consent Management [#consent-management]

```javascript
// Grant consent (enables storage)
window.VitalSentinelAnalytics.grantConsent('persistent');

// Revoke consent (clears storage)
window.VitalSentinelAnalytics.revokeConsent();

// Check current level
const level = window.VitalSentinelAnalytics.getConsentLevel();
// Returns: 'none', 'session', or 'persistent'
```

The consent level is not remembered between page loads, so your consent tool has to call `grantConsent()` on every page load. `revokeConsent()` sends whatever is already queued, drops back to `none`, clears the device and network details it had collected, and deletes the identifiers it had stored. It is forward-looking: events already collected are still sent, carrying the level they were collected under. `grantConsent()` is ignored for a visitor whose browser sends Do Not Track or Global Privacy Control.

### Opt Out/In [#opt-outin]

```javascript
// Stop all tracking
window.VitalSentinelAnalytics.optOut();

// Resume tracking
window.VitalSentinelAnalytics.optIn();
```

`optOut()` applies to the current page load only and is not remembered, so call it again on each page load.

### Force Flush [#force-flush]

```javascript
// Send all queued events immediately
window.VitalSentinelAnalytics.flush();
```

### Debug Info [#debug-info]

```javascript
// Logs the current tracking state (consent, session, queued events) for debugging
const debug = window.VitalSentinelAnalytics.getDebug();
console.log(debug);
```

## Data Attribute Event Tracking [#data-attribute-event-tracking]

Track events without JavaScript using data attributes:

```html
<button
  data-vs-event="button_click"
  data-vs-button-id="cta-header"
  data-vs-button-text="Sign Up"
>
  Sign Up Now
</button>

<a
  href="/pricing"
  data-vs-event="link_click"
  data-vs-link-type="navigation"
>
  View Pricing
</a>
```

When clicked, these automatically send events with the specified properties.

## Privacy & Consent [#privacy--consent]

### Storage Levels [#storage-levels]

The Web Analytics script supports three consent levels:

| Level            | Storage        | Behavior                                                                                                       |
| ---------------- | -------------- | -------------------------------------------------------------------------------------------------------------- |
| `none` (default) | None           | Nothing is stored on the device; visits are stitched together on our servers                                   |
| `session`        | sessionStorage | Session ID persists within the browser session (30-min timeout)                                                |
| `persistent`     | localStorage   | Visitor ID recognizes returning visitors. A first-party cookie is written only if you set `data-cookie-domain` |

With `persistent` storage, the visitor ID is kept for 180 days. If you set `data-cookie-domain`, the matching first-party cookie uses the same lifetime.

### Default Privacy [#default-privacy]

By default (`data-storage="none"`):

* No cookies are set
* No localStorage/sessionStorage is used
* No visitor ID is stored on the device, and none is sent
* The script stores nothing on the visitor's device, so it needs no consent banner for storage

Data collected in this mode is pseudonymous, not anonymous: pageviews from one person on one site are stitched into a visit on our servers.

### Granting Consent [#granting-consent]

When a user accepts cookies:

```javascript
// After user consents to analytics
window.VitalSentinelAnalytics.grantConsent('persistent');
```

### Cookie Consent Integration [#cookie-consent-integration]

Listen for consent events from your cookie banner and call grantConsent:

```javascript
window.addEventListener('cookie-consent-granted', () => {
  window.VitalSentinelAnalytics.grantConsent('persistent');
});
```

Or dispatch the built-in consent event (the script listens for this automatically):

```javascript
// When user consents
window.dispatchEvent(new CustomEvent('vs-analytics-consent', {
  detail: { level: 'persistent' }
}));
```

### Do Not Track and Global Privacy Control [#do-not-track-and-global-privacy-control]

The script reads `navigator.doNotTrack`, `window.doNotTrack`, `navigator.msDoNotTrack` and `navigator.globalPrivacyControl`. Any value that is present and is not `0`, `false` or `unspecified` counts as an opt-out.

For those visitors:

* No storage is read or written, whatever `data-storage` is set to
* No engagement measurement runs (scroll depth, active time, engagement score, rage clicks)
* `grantConsent()` is ignored, so your consent banner cannot raise the level for them
* The page view is still counted, so the traffic does not disappear from your reports

## Bot Detection [#bot-detection]

Bot traffic is excluded from your analytics: known bot user agents, headless browsers and automated testing tools. The detection runs on our servers, from the `User-Agent` request header every browser sends anyway, so it works in every storage mode. In the browser, the script checks `navigator.webdriver` and a few automation globals in every mode, and only reads `navigator.userAgent` when storage is set to `session` or `persistent`.

## Troubleshooting [#troubleshooting]

### No Page Views Appearing [#no-page-views-appearing]

1. Check browser console for errors
2. Verify tracking ID is correct
3. Ensure script is loading (Network tab)
4. Check if an ad blocker is active

### E-commerce Not Tracking [#e-commerce-not-tracking]

1. Verify you're using a supported platform (Shopify, WooCommerce, Magento, Squarespace)
2. Check that product pages have the expected structure
3. Enable debug mode (`data-debug="true"`) to see detection in console logs

### Debug Mode [#debug-mode]

Enable debug logging:

```html
<script
  src="https://analytics.vitalsentinel.com/analytics.js"
  data-key="YOUR_TRACKING_ID"
  data-debug="true"
  async
></script>
```

Check browser console for `[VitalSentinel Analytics]` messages.

### Duplicate Page Views [#duplicate-page-views]

For SPAs, ensure you're not manually calling `trackPageView()` when automatic detection is enabled. Either:

* Use `data-spa="false"` and track manually, or
* Use `data-spa="true"` and let auto-detection handle it

## Browser Compatibility [#browser-compatibility]

The Web Analytics script supports:

* Chrome/Edge 90+
* Firefox 89+
* Safari 15+
* Opera 76+

Older browsers will gracefully degrade without causing errors.

## Related [#related]

<Cards>
  <Card title="Web Analytics" href="/features/web-analytics">
    Explore the analytics dashboard and available reports
  </Card>

  <Card title="Engagement Analytics" href="/features/engagement-analytics">
    Deep dive into scroll depth, rage clicks, and user behavior
  </Card>

  <Card title="Data & Privacy" href="/others/data-privacy">
    Details on data collection, storage, and GDPR compliance
  </Card>

  <Card title="Troubleshooting" href="/help/troubleshooting">
    Solutions to common installation and tracking issues
  </Card>
</Cards>
