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

# Pageview Tracking

> Track pageviews and user engagement with the Permutive JavaScript SDK

The web addon provides automatic pageview tracking, engagement measurement, and standard event capture. This is the recommended way to track user activity.

<CardGroup cols={3}>
  <Card title="Web Addon" href="#web-addon" icon="puzzle-piece" />

  <Card title="Page Properties" href="#page-properties" icon="list" />

  <Card title="SPA Support" href="#spa-support" icon="arrows-rotate" />
</CardGroup>

## Web Addon

The web addon automatically tracks:

* **Pageview** - When a page loads
* **PageviewEngagement** - Periodic engagement signals
* **PageviewComplete** - When user leaves the page
* **FormSubmission** - When forms are submitted
* **LinkClick** - When links are clicked

### Basic Setup

```javascript theme={"dark"}
permutive.addon('web', {
  page: {
    type: 'article'
  }
});
```

This single call enables all automatic tracking features.

### With Page Properties

```javascript theme={"dark"}
permutive.addon('web', {
  page: {
    type: 'article',
    article: {
      title: 'Article Title',
      categories: ['news', 'technology'],
      authors: ['Jane Smith'],
      publishedAt: '2024-01-15T10:00:00Z'
    }
  }
});
```

## Page Properties

Page properties provide context about the content and are included in Pageview events.

### Standard Properties

| Property       | Type   | Description                                   |
| -------------- | ------ | --------------------------------------------- |
| `page.type`    | string | Page type (article, homepage, section, video) |
| `page.article` | object | Article-specific metadata                     |
| `page.video`   | object | Video-specific metadata                       |
| `page.section` | object | Section/category metadata                     |

### Article Properties

```javascript theme={"dark"}
permutive.addon('web', {
  page: {
    type: 'article',
    article: {
      // Identification
      id: 'article-123',
      title: 'Breaking News: Tech Advances',

      // Categorization
      categories: ['technology', 'business'],
      tags: ['AI', 'startups', 'innovation'],
      section: 'Technology',

      // Attribution
      authors: ['Jane Smith', 'John Doe'],
      publishedAt: '2024-01-15T10:00:00Z',
      modifiedAt: '2024-01-15T14:30:00Z',

      // Content attributes
      wordCount: 1500,
      readTime: 7,
      premium: false,
      sponsored: false
    }
  }
});
```

### Video Properties

```javascript theme={"dark"}
permutive.addon('web', {
  page: {
    type: 'video',
    video: {
      id: 'video-456',
      title: 'Product Demo',
      duration: 180,
      categories: ['product', 'tutorial'],
      series: 'How-To Guides',
      season: 1,
      episode: 5
    }
  }
});
```

### Homepage

```javascript theme={"dark"}
permutive.addon('web', {
  page: {
    type: 'homepage'
  }
});
```

### Section Page

```javascript theme={"dark"}
permutive.addon('web', {
  page: {
    type: 'section',
    section: {
      name: 'Technology',
      path: '/technology',
      level: 1
    }
  }
});
```

## Automatic Events

### Pageview Event

Fired immediately when `addon('web')` is called:

```javascript theme={"dark"}
// Event structure
{
  name: 'Pageview',
  properties: {
    type: 'article',
    article: {
      title: 'Article Title',
      categories: ['news']
    },
    client: {
      url: 'https://example.com/article',
      referrer: 'https://google.com',
      user_agent: '...'
    }
  }
}
```

### PageviewEngagement Event

Fired periodically while the user is on the page:

```javascript theme={"dark"}
// Event structure
{
  name: 'PageviewEngagement',
  properties: {
    engaged_time: 30,  // seconds
    completion: 0.5    // scroll depth (0-1)
  }
}
```

### PageviewComplete Event

Fired when the user leaves the page:

```javascript theme={"dark"}
// Event structure
{
  name: 'PageviewComplete',
  properties: {
    total_engaged_time: 120,
    completion: 0.85
  }
}
```

### FormSubmission Event

Fired when a form is submitted:

```javascript theme={"dark"}
// Event structure
{
  name: 'FormSubmission',
  properties: {
    form_id: 'newsletter-signup',
    form_name: 'Newsletter',
    // Form data (excluding sensitive fields)
  }
}
```

<Info>
  Sensitive fields (password, credit card, etc.) are automatically excluded from FormSubmission events.
</Info>

### LinkClick Event

Fired for outbound link clicks:

```javascript theme={"dark"}
// Event structure
{
  name: 'LinkClick',
  properties: {
    href: 'https://external-site.com/page',
    text: 'Click here'
  }
}
```

## Engagement Configuration

### Engagement Interval

Control how often PageviewEngagement events fire:

```javascript theme={"dark"}
permutive.addon('web', {
  page: { type: 'article' },
  eventInterval: 5000  // milliseconds (default: 5000)
});
```

### Custom Engagement Detection

Override the default engagement detection:

```javascript theme={"dark"}
permutive.addon('web', {
  page: { type: 'article' },
  addPageEngagementDetection: function(callback) {
    // Custom engagement detection
    // Call callback() when user is engaged
    document.addEventListener('mousemove', callback);
    document.addEventListener('scroll', callback);
    document.addEventListener('keypress', callback);

    // Return cleanup function
    return function() {
      document.removeEventListener('mousemove', callback);
      document.removeEventListener('scroll', callback);
      document.removeEventListener('keypress', callback);
    };
  }
});
```

### Custom Completion Logic

Override the default scroll completion calculation:

```javascript theme={"dark"}
permutive.addon('web', {
  page: { type: 'article' },
  getPageCompletion: function() {
    // Custom completion calculation
    var scrollTop = window.pageYOffset;
    var docHeight = document.documentElement.scrollHeight;
    var winHeight = window.innerHeight;
    return scrollTop / (docHeight - winHeight);
  }
});
```

## Context Override

Override automatic context detection:

```javascript theme={"dark"}
permutive.addon('web', {
  context: {
    url: 'https://example.com/canonical-url',
    title: 'Custom Page Title',
    referrer: 'https://custom-referrer.com'
  },
  page: { type: 'article' }
});
```

## SPA Support

For Single Page Applications (React, Vue, Angular), call `permutive.addon('web', {...})` on navigation to track a new Pageview:

### Basic SPA Pattern

```javascript theme={"dark"}
// Initial page load
permutive.addon('web', {
  page: getPageProperties()
});

// On navigation
function navigateToNewPage() {
  permutive.addon('web', {
    page: getNewPageProperties()
  });
}
```

### React Integration

```javascript theme={"dark"}
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function PermutiveTracker() {
  const location = useLocation();

  useEffect(() => {
    // Track pageview on initial load and route changes
    permutive.addon('web', {
      page: getPageProperties(location)
    });
  }, [location]);

  return null;
}

function getPageProperties(location) {
  return {
    type: 'article',
    article: {
      title: document.title,
      // ... other properties
    }
  };
}
```

### Vue Integration

```javascript theme={"dark"}
// In router/index.js or main.js
import router from './router';

router.afterEach((to, from) => {
  // Track pageview on every navigation
  permutive.addon('web', {
    page: getPageProperties(to)
  });
});

function getPageProperties(route) {
  return {
    type: route.meta.pageType || 'page',
    // ... other properties
  };
}
```

### Angular Integration

```typescript theme={"dark"}
// In app.component.ts
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';

@Component({...})
export class AppComponent {
  constructor(private router: Router) {
    this.router.events
      .pipe(filter(event => event instanceof NavigationEnd))
      .subscribe((event: NavigationEnd) => {
        this.trackPageview(event.urlAfterRedirects);
      });
  }

  private trackPageview(url: string): void {
    const pageProps = this.getPageProperties(url);

    // Track pageview on every navigation
    (window as any).permutive.addon('web', {
      page: pageProps
    });
  }
}
```

## Infinite Scroll

For infinite scroll pages, track additional content loads:

```javascript theme={"dark"}
var webAddon = permutive.addon('web', {
  page: { type: 'feed' },
  dirtyEvents: ['contentLoaded']  // Custom dirty events
});

// When new content loads
function onNewContentLoaded() {
  // Mark page as "dirty" to trigger new engagement tracking
  webAddon.markDirty('contentLoaded');
}
```

## Filtering Events

Filter which events are tracked:

```javascript theme={"dark"}
permutive.addon('web', {
  page: { type: 'article' },
  filterPermutiveEvent: {
    // Only track form submissions with certain IDs
    FormSubmission: function(event) {
      return event.target.id !== 'search-form';
    },
    // Only track external links
    LinkClick: function(event) {
      var href = event.target.href;
      return href && !href.includes(window.location.hostname);
    }
  }
});
```

## Debugging

Enable debug mode to see pageview tracking in action:

```
?__permutive.loggingEnabled=true
```

Console output:

```
[Permutive] Web addon initialized
[Permutive] Tracking: Pageview {type: "article", ...}
[Permutive] Event accepted: 1/1
[Permutive] Tracking: PageviewEngagement {engaged_time: 5}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Pageview not tracking">
    **Problem:** No Pageview event in debug output.

    **Solutions:**

    * Verify `permutive.addon('web', {...})` is called
    * Check that consent is granted (if `consentRequired: true`)
    * Ensure SDK is loaded before addon call
    * Look for JavaScript errors in console
  </Accordion>

  <Accordion title="Engagement not tracking">
    **Problem:** No PageviewEngagement events.

    **Solutions:**

    * Check `eventInterval` isn't set too high
    * Verify user is actively engaging (scroll, mouse move)
    * Ensure page stays open long enough
    * Test with debug mode enabled
  </Accordion>

  <Accordion title="SPA navigation not tracking">
    **Problem:** Only first pageview tracked in SPA.

    **Solutions:**

    * Call `permutive.addon('web', { page: {...} })` on each route change
    * Verify the addon call receives updated page properties
    * Check that router events are firing correctly
  </Accordion>

  <Accordion title="Duplicate pageviews">
    **Problem:** Multiple Pageview events per page.

    **Solutions:**

    * Ensure `addon('web')` called only once
    * Check for multiple SDK initializations
    * Verify not calling both addon and track manually
  </Accordion>
</AccordionGroup>

## Related Documentation

<CardGroup cols={2}>
  <Card title="Event Tracking" icon="bolt" href="/sdks/web/javascript-sdk/features/event-tracking">
    Track custom events
  </Card>

  <Card title="Event Properties" icon="list" href="/sdks/web/javascript-sdk/core-concepts/event-properties">
    Structure event data
  </Card>

  <Card title="Contextual Data" icon="bullseye" href="/sdks/web/javascript-sdk/core-concepts/contextual-data">
    Page-based targeting
  </Card>

  <Card title="Video Tracking" icon="video" href="/sdks/web/javascript-sdk/features/video-tracking">
    Track video content
  </Card>
</CardGroup>
