FullStory Event Tracking | Blue Frog Docs

FullStory Event Tracking

Comprehensive guide to tracking custom events, user actions, and behaviors in FullStory using the FS.event API and FS.identify.

FullStory Event Tracking

Overview

Event tracking in FullStory allows you to capture custom user actions, business events, and behavioral signals beyond automatic page views and clicks. By implementing custom events, you can track specific interactions like button clicks, form submissions, feature usage, errors, and business outcomes.

FullStory's event tracking is powered by the FS.event() API, which lets you send custom events with properties and metadata. Combined with FS.identify() for user identification, you can build rich behavioral datasets that power Omnisearch, funnels, segments, and session replay filters.

Why Track Custom Events?

While FullStory automatically captures clicks, page views, and form interactions, custom events let you track:

  • Business-specific actions: Add to cart, checkout, subscription upgrades
  • Feature usage: Toggle settings, open modals, use search
  • User milestones: Complete onboarding, reach usage limits, achieve goals
  • Error states: API failures, validation errors, timeout issues
  • Custom workflows: Multi-step processes, conditional logic, A/B test variants

Custom events make your FullStory data more meaningful and actionable by aligning tracking with your product's unique behaviors.

The FS.event() API

Basic Syntax

FS.event(eventName, eventProperties);

Parameters:

  • eventName (string, required): The name of the event (e.g., "Clicked CTA Button")
  • eventProperties (object, optional): Key-value pairs with additional context

Simple Event Example

Track a button click:

FS.event('Signup Button Clicked');

This creates a searchable event in FullStory. You can later search for sessions where this event occurred using Omnisearch.

Event with Properties

Track a button click with additional context:

FS.event('Signup Button Clicked', {
  location: 'homepage',
  button_color: 'blue',
  campaign: 'summer_promo'
});

Event properties allow you to filter and segment events by specific attributes. For example, you can search for "Signup Button Clicked where location is homepage."

Event Naming Best Practices

Use clear, descriptive names:

// Good
FS.event('Checkout Started');
FS.event('Video Played');
FS.event('Filter Applied');

// Bad
FS.event('click');
FS.event('event1');
FS.event('action');

Use consistent naming conventions:

// Consistent pattern: Action + Object
FS.event('Clicked Pricing CTA');
FS.event('Submitted Contact Form');
FS.event('Viewed Product Page');

Avoid special characters:

// Good
FS.event('Add to Cart');

// Bad
FS.event('Add-to-Cart!');
FS.event('add_to_cart#123');

Common Event Tracking Patterns

Button Clicks

Track specific button interactions:

document.getElementById('cta-button').addEventListener('click', function() {
  FS.event('CTA Button Clicked', {
    button_text: this.innerText,
    page: window.location.pathname,
    timestamp: new Date().toISOString()
  });
});

Form Submissions

Track form submissions with context:

document.getElementById('contact-form').addEventListener('submit', function(e) {
  FS.event('Contact Form Submitted', {
    form_name: 'Contact Us',
    page: window.location.pathname,
    fields_completed: document.querySelectorAll('input:valid').length
  });
});

Feature Usage

Track when users interact with specific features:

function toggleDarkMode() {
  const isDarkMode = document.body.classList.toggle('dark-mode');

  FS.event('Dark Mode Toggled', {
    enabled: isDarkMode,
    source: 'settings_menu'
  });
}

E-commerce Events

Track shopping behaviors:

// Add to cart
function addToCart(product) {
  FS.event('Product Added to Cart', {
    product_id: product.id,
    product_name: product.name,
    price: product.price,
    quantity: 1,
    category: product.category
  });
}

// Checkout started
function startCheckout(cart) {
  FS.event('Checkout Started', {
    cart_value: cart.total,
    item_count: cart.items.length,
    currency: 'USD'
  });
}

// Purchase completed
function completePurchase(order) {
  FS.event('Purchase Completed', {
    order_id: order.id,
    revenue: order.total,
    items: order.items.length,
    payment_method: order.paymentMethod
  });
}

Error Tracking

Track errors and failures:

// API Error
fetch('/api/data')
  .then(response => {
    if (!response.ok) {
      FS.event('API Error', {
        endpoint: '/api/data',
        status_code: response.status,
        error_message: response.statusText
      });
    }
    return response.json();
  })
  .catch(error => {
    FS.event('Network Error', {
      endpoint: '/api/data',
      error: error.message
    });
  });

// Form Validation Error
function validateForm(form) {
  const errors = getFormErrors(form);

  if (errors.length > 0) {
    FS.event('Form Validation Error', {
      form_name: form.id,
      error_count: errors.length,
      first_error: errors[0]
    });
  }
}

Video Interactions

Track video engagement:

const video = document.getElementById('promo-video');

video.addEventListener('play', function() {
  FS.event('Video Played', {
    video_id: video.dataset.videoId,
    video_title: video.dataset.title,
    duration: video.duration
  });
});

video.addEventListener('ended', function() {
  FS.event('Video Completed', {
    video_id: video.dataset.videoId,
    watch_time: video.currentTime
  });
});

video.addEventListener('pause', function() {
  const percentWatched = (video.currentTime / video.duration) * 100;

  FS.event('Video Paused', {
    video_id: video.dataset.videoId,
    percent_watched: Math.round(percentWatched),
    timestamp: video.currentTime
  });
});

Search Queries

Track search behavior:

document.getElementById('search-form').addEventListener('submit', function(e) {
  const query = document.getElementById('search-input').value;

  FS.event('Search Performed', {
    query: query,
    query_length: query.length,
    page: window.location.pathname
  });
});

// Track search result clicks
function trackSearchResultClick(result, position) {
  FS.event('Search Result Clicked', {
    result_title: result.title,
    result_position: position,
    query: getCurrentSearchQuery()
  });
}

User Identification with FS.identify()

Overview

FS.identify() associates user sessions with specific user identities and custom attributes. This allows you to:

  • Search for sessions by specific users
  • Filter replays by user properties (account type, subscription, company)
  • Segment analytics by user cohorts
  • Link FullStory data to your CRM or database

Basic Syntax

FS.identify(userId, userVars);

Parameters:

  • userId (string, required): Unique identifier for the user (email, database ID, etc.)
  • userVars (object, optional): Custom user properties

Simple Identification

Identify a logged-in user:

FS.identify('user_12345');

Identification with User Properties

Enrich user identification with custom attributes:

FS.identify('user_12345', {
  displayName: 'Jane Doe',
  email: 'jane@example.com',
  accountType: 'premium',
  signupDate: '2024-01-15',
  company: 'Acme Corp',
  plan: 'enterprise',
  monthlySpend: 499
});

User Property Types

FullStory supports several property types:

String properties:

FS.identify('user_123', {
  email_str: 'user@example.com',
  plan_str: 'premium',
  role_str: 'admin'
});

Number properties:

FS.identify('user_123', {
  loginCount_int: 42,
  accountAge_int: 365,
  revenue_real: 1250.50
});

Boolean properties:

FS.identify('user_123', {
  isPremium_bool: true,
  hasCompletedOnboarding_bool: true,
  isTrialing_bool: false
});

Date properties:

FS.identify('user_123', {
  signupDate_date: new Date('2024-01-15'),
  lastLogin_date: new Date()
});

When to Call FS.identify()

After user login:

function onUserLogin(user) {
  FS.identify(user.id, {
    email: user.email,
    displayName: user.name,
    accountType: user.accountType,
    lastLogin: new Date()
  });
}

On page load for logged-in users:

// Check if user is logged in
if (currentUser) {
  FS.identify(currentUser.id, {
    email: currentUser.email,
    plan: currentUser.subscription.plan,
    role: currentUser.role
  });
}

When user properties change:

function onSubscriptionUpgrade(newPlan) {
  FS.identify(currentUser.id, {
    plan: newPlan,
    upgradeDate: new Date()
  });
}

Updating User Properties

You can update user properties at any time:

// Initial identification
FS.identify('user_123', {
  plan: 'free',
  trialEnded: false
});

// Later, when user upgrades
FS.identify('user_123', {
  plan: 'premium',
  upgradeDate: new Date()
});

Anonymous Users

For anonymous users (not logged in), FullStory automatically assigns a unique session ID. You can still track events without calling FS.identify():

// No identification needed for anonymous users
FS.event('Viewed Pricing Page');
FS.event('Started Free Trial');

Once the user logs in, call FS.identify() to associate previous anonymous sessions with their identity.

Advanced Event Tracking Patterns

Conditional Event Tracking

Track events only under specific conditions:

function trackFeatureUsage(featureName) {
  // Only track for premium users
  if (currentUser.plan === 'premium') {
    FS.event('Premium Feature Used', {
      feature: featureName,
      userId: currentUser.id
    });
  }
}

Batching Events

Track related events together:

function trackCheckoutFlow(cart) {
  // Event 1: Checkout started
  FS.event('Checkout Started', {
    cartValue: cart.total,
    itemCount: cart.items.length
  });

  // Event 2: Payment method selected
  FS.event('Payment Method Selected', {
    method: cart.paymentMethod
  });

  // Event 3: Order completed
  FS.event('Order Completed', {
    orderId: cart.orderId,
    revenue: cart.total
  });
}

Tracking User Journeys

Track multi-step processes:

// Step 1: Onboarding started
FS.event('Onboarding Started', {
  source: 'signup_page'
});

// Step 2: Profile completed
FS.event('Onboarding Profile Completed', {
  fields_completed: 8
});

// Step 3: Tutorial completed
FS.event('Onboarding Tutorial Completed', {
  steps_completed: 5
});

// Step 4: Onboarding finished
FS.event('Onboarding Completed', {
  duration_seconds: 180
});

A/B Test Tracking

Track experiment variants:

// Assign user to variant
const variant = assignExperimentVariant(currentUser.id);

FS.identify(currentUser.id, {
  experiment_variant: variant,
  experiment_name: 'pricing_page_test'
});

// Track variant-specific events
FS.event('Experiment CTA Clicked', {
  experiment: 'pricing_page_test',
  variant: variant
});

Framework-Specific Integration

React

import { useEffect } from 'react';

function ProductPage({ product }) {
  useEffect(() => {
    FS.event('Product Page Viewed', {
      product_id: product.id,
      product_name: product.name,
      price: product.price
    });
  }, [product]);

  const handleAddToCart = () => {
    FS.event('Add to Cart Clicked', {
      product_id: product.id,
      source: 'product_page'
    });
    // ... add to cart logic
  };

  return (
    <div>
      <button onClick={handleAddToCart}>Add to Cart</button>
    </div>
  );
}

Vue.js

export default {
  methods: {
    trackEvent(eventName, properties) {
      if (window.FS) {
        window.FS.event(eventName, properties);
      }
    },

    handleCheckout() {
      this.trackEvent('Checkout Button Clicked', {
        cart_value: this.cart.total,
        items: this.cart.items.length
      });
      // ... checkout logic
    }
  },

  mounted() {
    this.trackEvent('Page Viewed', {
      page: this.$route.name
    });
  }
}

Angular

import { Component, OnInit } from '@angular/core';

declare global {
  interface Window {
    FS: any;
  }
}

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html'
})
export class ProductComponent implements OnInit {

  ngOnInit() {
    this.trackEvent('Product Page Loaded', {
      product_id: this.productId
    });
  }

  trackEvent(eventName: string, properties?: any) {
    if (window.FS) {
      window.FS.event(eventName, properties);
    }
  }

  addToCart() {
    this.trackEvent('Add to Cart', {
      product_id: this.productId,
      price: this.product.price
    });
  }
}

Event Tracking Best Practices

Define an Event Taxonomy

Create a consistent naming structure:

Format: [Action] [Object] [Context]

Examples:

  • Clicked CTA Button - Homepage
  • Submitted Form - Contact
  • Viewed Page - Pricing
  • Completed Checkout - Success

Limit Event Properties

Keep properties focused and relevant:

// Good: Clear, focused properties
FS.event('Video Played', {
  video_id: '12345',
  duration: 120,
  autoplay: false
});

// Bad: Too many unnecessary properties
FS.event('Video Played', {
  video_id: '12345',
  duration: 120,
  autoplay: false,
  browser: 'Chrome',
  os: 'Windows',
  screen_width: 1920,
  timestamp: Date.now(),
  user_agent: navigator.userAgent
  // FullStory already captures most of this automatically
});

Avoid PII in Events

Don't send personally identifiable information in event properties:

// Bad: Contains PII
FS.event('Form Submitted', {
  email: 'user@example.com',
  phone: '555-1234',
  ssn: '123-45-6789'
});

// Good: No PII
FS.event('Form Submitted', {
  form_type: 'contact',
  fields_count: 5
});

Test Events Before Deploying

Verify events fire correctly in development:

function trackEvent(name, properties) {
  if (window.FS) {
    console.log('[FullStory Event]', name, properties);
    FS.event(name, properties);
  } else {
    console.warn('[FullStory] Not loaded, event not tracked:', name);
  }
}

Document Your Events

Maintain a tracking plan document:

# FullStory Event Tracking Plan

## Signup Flow
- **Event:** Signup Button Clicked
  - **When:** User clicks "Sign Up" CTA
  - **Properties:** location (string), button_color (string)

- **Event:** Signup Form Submitted
  - **When:** User submits registration form
  - **Properties:** form_type (string), plan_selected (string)

Searching Events in FullStory

Once events are tracked, use Omnisearch to find sessions:

Search by event name:

Event: "Checkout Started"

Search by event property:

Event: "Product Added to Cart" where product_category = "electronics"

Combine events with user properties:

Event: "Checkout Completed" where plan = "premium"

Find users who performed multiple events:

Event: "Signup Button Clicked" AND Event: "Signup Completed"

Troubleshooting Event Tracking

Events Not Appearing in FullStory

Check if FullStory is loaded:

if (window.FS) {
  console.log('FullStory loaded');
  FS.event('Test Event');
} else {
  console.error('FullStory not loaded');
}

Verify event syntax:

// Correct
FS.event('Button Clicked', { location: 'homepage' });

// Incorrect (missing event name)
FS.event({ location: 'homepage' });

User Identification Not Working

Ensure userId is a string:

// Correct
FS.identify('12345');

// Incorrect (number instead of string)
FS.identify(12345);

Check timing:

// Make sure FS is loaded before identifying
window.addEventListener('load', function() {
  if (window.FS && currentUser) {
    FS.identify(currentUser.id, {
      email: currentUser.email
    });
  }
});

Events Firing Multiple Times

Use event listeners carefully to avoid duplicates:

// Bad: Adds listener every time function runs
function setupTracking() {
  button.addEventListener('click', () => {
    FS.event('Button Clicked');
  });
}

// Good: Only add listener once
let listenerAdded = false;

function setupTracking() {
  if (!listenerAdded) {
    button.addEventListener('click', () => {
      FS.event('Button Clicked');
    });
    listenerAdded = true;
  }
}

Next Steps:

Additional Resources:

// SYS.FOOTER