Custom event goals let you track any action by firing JavaScript events. Perfect for forms, video plays, and complex interactions.
When to Use Custom Events
Best for tracking:
- Form submissions (AJAX)
- Video interactions (play, complete)
- Single-page app navigation
- Add to cart actions
- Feature usage in web apps
- Any JavaScript-triggered action
How Custom Events Work
- You add JavaScript code to your site
- Code fires when action occurs
- Zenovay receives the event
- Goal is recorded
Basic Implementation
Tracking an Event
// Simple event
zenovay('goal', 'event_name');
// Event with value
zenovay('goal', 'purchase', { value: 99.99 });
// Event with properties
zenovay('goal', 'signup', {
value: 50,
plan: 'pro',
source: 'homepage'
});
Creating the Goal
- Go to Goals and click New Goal
- Select "Custom Event"
- Enter event name (e.g.,
purchase) - Set name and value options
- Save
Event Naming
Best Practices
| Good Names | Bad Names |
|---|---|
signup_complete | goal1 |
purchase | click |
video_50_percent | event |
add_to_cart | 123 |
Naming Conventions
Choose a consistent pattern:
snake_case:form_submitcamelCase:formSubmitkebab-case:form-submit
Categories with Prefixes
Organize events with prefixes:
// E-commerce events
zenovay('goal', 'ecom_add_to_cart');
zenovay('goal', 'ecom_checkout_start');
zenovay('goal', 'ecom_purchase');
// Engagement events
zenovay('goal', 'engage_video_play');
zenovay('goal', 'engage_scroll_50');
Common Implementations
Form Submission
document.querySelector('form').addEventListener('submit', function(e) {
zenovay('goal', 'contact_form');
});
// Or for AJAX forms
function onFormSuccess() {
zenovay('goal', 'contact_form');
}
Video Interactions
const video = document.querySelector('video');
video.addEventListener('play', function() {
zenovay('goal', 'video_play');
});
video.addEventListener('ended', function() {
zenovay('goal', 'video_complete');
});
// Track 50% progress
video.addEventListener('timeupdate', function() {
if (video.currentTime / video.duration > 0.5) {
zenovay('goal', 'video_50_percent');
}
});
E-commerce Actions
// Add to cart
function addToCart(product) {
// Your cart logic...
zenovay('goal', 'add_to_cart', {
value: product.price,
product_id: product.id
});
}
// Purchase complete
function onPurchaseComplete(order) {
zenovay('goal', 'purchase', {
value: order.total,
order_id: order.id,
items: order.items.length
});
}
Sign Up / Registration
async function handleSignup(formData) {
try {
const response = await api.signup(formData);
if (response.success) {
zenovay('goal', 'signup_complete', {
value: 50, // Estimated lead value
plan: formData.plan
});
}
} catch (error) {
// Handle error
}
}
Passing Values
Static Value
Set in Zenovay dashboard:
- Goal value: $50
- Every event = $50
Dynamic Value
Pass from your code:
// Order value
zenovay('goal', 'purchase', {
value: orderTotal
});
// Calculated value
const leadValue = isPremium ? 100 : 25;
zenovay('goal', 'lead', { value: leadValue });
Value from Page
Read from DOM:
const price = parseFloat(
document.querySelector('.price').textContent.replace('$', '')
);
zenovay('goal', 'purchase', { value: price });
Event Properties
Passing Properties
Include additional data:
zenovay('goal', 'signup', {
value: 50,
plan: 'pro',
source: 'homepage',
referrer: document.referrer
});
Property Types
| Property | Type | Example |
|---|---|---|
| value | number | 99.99 |
| currency | string | "USD" |
| product_id | string | "SKU123" |
| category | string | "electronics" |
| quantity | number | 2 |
Using Properties in Reports
Scale PlanProperties enable:
- Filtering by property
- Grouping by category
- Custom segmentation
Framework Integrations
React
import { useCallback } from 'react';
function SignupForm() {
const handleSubmit = useCallback((data) => {
// Submit logic...
window.zenovay('goal', 'signup');
}, []);
return <form onSubmit={handleSubmit}>...</form>;
}
Vue
<script>
export default {
methods: {
handleSubmit() {
// Submit logic...
window.zenovay('goal', 'signup');
}
}
}
</script>
Next.js
// In your component or API route
export default function handler(req, res) {
// Server logic...
// Client-side tracking (in component)
if (typeof window !== 'undefined') {
window.zenovay('goal', 'api_success');
}
}
Single Page Apps (SPAs)
Route Change Tracking
For virtual pageviews:
// React Router
history.listen(() => {
zenovay('page');
});
// Vue Router
router.afterEach(() => {
zenovay('page');
});
Goal After Navigation
// Navigate then track
router.push('/success').then(() => {
zenovay('goal', 'signup_complete');
});
Conditional Tracking
Track Based on Conditions
// Only track if conditions met
if (user.isNewUser && !user.hasConvertedBefore) {
zenovay('goal', 'first_conversion');
}
// A/B test variant
if (experimentVariant === 'B') {
zenovay('goal', 'variant_b_conversion');
}
Debouncing Events
Prevent duplicate fires:
let hasTracked = false;
function trackOnce(eventName) {
if (!hasTracked) {
zenovay('goal', eventName);
hasTracked = true;
}
}
Error Handling
Safe Tracking
Handle cases where Zenovay isn't loaded:
function safeTrackGoal(eventName, properties) {
if (typeof zenovay === 'function') {
zenovay('goal', eventName, properties);
} else {
console.warn('Zenovay not loaded');
}
}
Async Tracking
For critical conversions:
// Wait for Zenovay to load
function trackWhenReady(eventName, properties) {
if (window.zenovay) {
window.zenovay('goal', eventName, properties);
} else {
window.addEventListener('zenovay:ready', function() {
window.zenovay('goal', eventName, properties);
});
}
}
Testing Events
Debug Mode
Enable console logging:
zenovay('debug');
// Now track something
zenovay('goal', 'test_event');
// Check console for output
Verify in Dashboard
- Fire test event
- Go to Analytics → Real-time
- Verify goal appears
- Check goal value
Network Tab
- Open DevTools → Network
- Fire event
- Look for request to Zenovay
- Verify payload
Troubleshooting
Events Not Recording
Check script loaded:
console.log(typeof zenovay); // Should be 'function'
Check event name matches:
- Dashboard:
signup - Code:
zenovay('goal', 'signup')✓ - Code:
zenovay('goal', 'Signup')✗
Check for errors:
- Browser console for errors
- Network tab for failed requests
Value Not Recording
Ensure value is a number:
// Wrong
zenovay('goal', 'purchase', { value: "$99.99" });
// Correct
zenovay('goal', 'purchase', { value: 99.99 });