Custom Events
Track specific user interactions and behaviors beyond standard page views. Custom events help you understand how visitors engage with your content and achieve your business goals.
What Are Custom Events?
Custom events let you track specific actions users take on your website:
- Button clicks - CTA buttons, navigation, social sharing
- Form submissions - Contact forms, newsletters, signups
- File downloads - PDFs, images, documents
- Video interactions - Play, pause, completion
- E-commerce actions - Add to cart, purchase, checkout steps
- Custom interactions - Any behavior specific to your site
Basic Event Tracking
Once you’ve installed the Betterlytics tracking script or the @betterlytics/tracker
npm package, you can track custom events using the global betterlytics.event()
function.
Handling Async Script Loading (Recommended)
If you’ve installed the @betterlytics/tracker
npm package, skip this step
If you’re loading the analytics script asynchronously, and not using our npm package, add this small snippet before your analytics script to prevent timing issues:
<script>
window.betterlytics =
window.betterlytics || {
event: function () {
(window.betterlytics.q = window.betterlytics.q || []).push(arguments);
}
};
</script>
<script
async
src="https://betterlytics.io/analytics.js"
data-site-id="your-siteid"
data-server-url="https://betterlytics.io/track"
></script>
This ensures betterlytics.event()
calls work immediately, even before the main script finishes loading.
Simple Event Tracking
Here is what sending a custom event looks like:
// Track a basic event
betterlytics.event("button-click");
// Track an event with properties
betterlytics.event("newsletter-signup", {
source: "homepage",
newsletter: "weekly-digest",
});
The first argument to the function (“button-click” or “newsletter-signup”) is the name of your custom event that will be used to identify the event in your dashboard.
The second (optional) argument is an object of event properties that provide extra content about the interaction.
Event Properties
Custom event properties help you understand what happened, where, and why, turning raw events into meaningful insights.
For example:
betterlytics.event("product-view", {
productId: "abc123",
category: "electronics",
price: 299.99,
source: "search-results",
});
These properties let you answer questions like:
- Which product? (
productId: "abc123"
) - What category? (
category: "electronics"
) - How much did it cost? (
price: 299.99
) - From what source? (
source: "search-results"
)
You can use this pattern to track any kind of user interaction: button clicks, form submissions, file downloads, feature usage, and more. These key-value pairs help you segment and analyze your data more effectively, enabling you to answer business questions and optimize user experience.
Property Guidelines
- Use descriptive property names (e.g.
source
,category
,price
) - Keep values simple - strings, numbers, booleans work best
- Avoid deeply nested objects - flat structures are easier to query
- Never include personally identifiable information (PII)
What counts as PII?
Personally Identifiable Information (PII) includes any data that could identify a specific person, such as:
- Names, emails, phone numbers
- Addresses or precise locations
- IP addresses or device IDs
- Social security numbers, IDs
- Account numbers or usernames
Common Use Case Examples
Ready to view your custom events? After setting up custom event tracking, check out our guide on using the Events Dashboard to view and analyze your data for insights into user behavior.
Button Clicks - Track CTA buttons, navigation, and interactive elements
document.getElementById("cta-button").addEventListener("click", function () {
betterlytics.event("cta-click", {
buttonText: "Get Started",
location: "header",
});
});
Form Submissions - Track contact forms, newsletters, and signups
document
.getElementById("contact-form")
.addEventListener("submit", function (e) {
betterlytics.event("form-submit", {
formType: "contact",
source: "contact-page",
});
});
// Track form abandonment (user starts but doesn't submit)
document.getElementById("signup-form").addEventListener("input", function () {
if (!this.hasAttribute("data-started")) {
this.setAttribute("data-started", "true");
betterlytics.event("form-started", {
formType: "signup",
formSection: "newsletter",
});
}
});
File Downloads - Track PDFs, images, documents, and media
document.querySelectorAll('a[href$=".pdf"]').forEach((link) => {
link.addEventListener("click", function () {
betterlytics.event("file-download", {
fileName: this.getAttribute("href"),
fileType: "pdf",
});
});
});
// Track all downloadable file types
const downloadableExtensions = [
".pdf",
".doc",
".docx",
".zip",
".jpg",
".png",
];
document.querySelectorAll("a").forEach((link) => {
const href = link.getAttribute("href") || "";
if (downloadableExtensions.some((ext) => href.toLowerCase().endsWith(ext))) {
link.addEventListener("click", function () {
const extension = href.split(".").pop().toLowerCase();
betterlytics.event("file-download", {
fileName: href.split("/").pop(),
fileType: extension,
fileSize: this.getAttribute("data-size") || "unknown",
});
});
}
});
Video Interactions - Track play, pause, completion, and engagement
const video = document.getElementById("promo-video");
video.addEventListener("play", () => {
betterlytics.event("video-play", { videoId: "promo-video" });
});
video.addEventListener("ended", () => {
betterlytics.event("video-complete", { videoId: "promo-video" });
});
// Advanced video tracking with progress milestones
let milestones = [25, 50, 75];
video.addEventListener("timeupdate", function () {
const progress = Math.round((this.currentTime / this.duration) * 100);
milestones.forEach((milestone, index) => {
if (
progress >= milestone &&
!this.hasAttribute(`data-milestone-${milestone}`)
) {
this.setAttribute(`data-milestone-${milestone}`, "true");
betterlytics.event("video-progress", {
videoId: this.id || "unnamed-video",
milestone: milestone,
duration: Math.round(this.duration),
});
}
});
});
E-commerce Actions - Track cart, checkout, and purchase events
// Add to cart
document.querySelectorAll(".add-to-cart").forEach((button) => {
button.addEventListener("click", function () {
betterlytics.event("add-to-cart", {
productId: this.getAttribute("data-product-id"),
productName: this.getAttribute("data-product-name"),
price: parseFloat(this.getAttribute("data-price")),
category: this.getAttribute("data-category"),
});
});
});
// Product wishlist actions
document.querySelectorAll(".add-to-wishlist").forEach((button) => {
button.addEventListener("click", function () {
betterlytics.event("add-to-wishlist", {
productId: this.getAttribute("data-product-id"),
productName: this.getAttribute("data-product-name"),
price: parseFloat(this.getAttribute("data-price")),
category: this.getAttribute("data-category"),
});
});
});
// Product comparison
function trackProductComparison(productIds) {
betterlytics.event("product-comparison", {
productCount: productIds.length,
productIds: productIds.join(","),
category: "electronics",
});
}
Search & Navigation - Track searches, filters, and user journey
// Search tracking
document.getElementById("search-form").addEventListener("submit", function (e) {
const query = this.querySelector('input[type="search"]').value;
betterlytics.event("search", {
query: query,
resultsCount: document.querySelectorAll(".search-result").length,
source: "header-search",
});
});
Framework-Specific Examples
React - Component-based event tracking with hooks
import { useEffect } from "react";
function ProductPage({ productId }) {
useEffect(() => {
betterlytics.event("product-view", {
productId: productId,
});
}, [productId]);
const handleAddToCart = () => {
betterlytics.event("add-to-cart", {
productId: productId,
source: "product-page",
});
};
return (
<div>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}
Vue.js - Reactive event tracking
<template>
<button @click="trackClick">Get Started</button>
</template>
<script>
export default {
methods: {
trackClick() {
betterlytics.event("cta-click", { component: "CTAButton" });
},
},
};
</script>
Angular - Service-based event tracking with dependency injection
// analytics.service.ts
import { Injectable } from "@angular/core";
declare global {
interface Window {
betterlytics: (eventName: string, eventProps?: Record<string, any>) => void;
}
}
@Injectable({
providedIn: "root",
})
export class AnalyticsService {
track(eventName: string, properties?: Record<string, any>) {
if (typeof window !== "undefined" && window.betterlytics) {
window.betterlytics.event(eventName, properties);
}
}
}
// component usage
import { Component } from "@angular/core";
import { AnalyticsService } from "./analytics.service";
@Component({
selector: "app-product",
template: `<button (click)="addToCart()">Add to Cart</button>`,
})
export class ProductComponent {
constructor(private analytics: AnalyticsService) {}
addToCart() {
this.analytics.track("add-to-cart", {
productId: this.productId,
source: "product-page",
});
}
}
Next.js - Component-rendered event tracking
// app/product/[id]/page.tsx
'use client';
import { useEffect } from "react";
export default function ProductPage({ params }: { params: { id: string } }) {
useEffect(() => {
betterlytics.event("page-view", {
pageType: "product",
productId: params.id,
});
}, [params.id]);
return <div>Product {params.id}</div>;
}
Nuxt.js - Component-based event tracking
<template>
<button @click="trackPurchase">Complete Purchase</button>
</template>
<script setup>
const trackPurchase = () => {
betterlytics.event("purchase", {
value: 99.99,
currency: "USD",
source: "checkout-page",
});
};
</script>
TypeScript Support
For TypeScript projects, you can add type definitions by creating a types/analytics.d.ts
file:
declare global {
interface Window {
betterlytics: (eventName: string, eventProps?: Record<string, any>) => void;
}
}
declare function betterlytics.event(
eventName: string,
eventProps?: Record<string, any>
): void;
export {};
Next Steps: After implementing custom events, learn how to view and analyze your event data in your Betterlytics dashboard.
Questions about custom events? Join our Discord community  for help!