Integrate Zenovay analytics into any JavaScript framework or vanilla JavaScript application using our flexible tracking API.
Universal Script Installation
Basic Script Tag
Works with any framework:
<script
defer
data-tracking-code="YOUR_TRACKING_CODE"
src="https://api.zenovay.com/z.js"
></script>
Script Attributes
| Attribute | Description |
|---|---|
data-tracking-code | Required. Your website tracking code |
defer | Recommended. Load script without blocking page render |
Standard Configuration
<script
defer
data-tracking-code="YOUR_TRACKING_CODE"
src="https://api.zenovay.com/z.js"
></script>
Vanilla JavaScript
Basic Usage
// Track an event
window.zenovay('track', 'button_click', {
button_id: 'signup',
location: 'hero'
});
// Track page view (if auto-track disabled)
window.zenovay('page');
// Identify a user
window.zenovay('identify', 'user-123', {
email: 'user@example.com',
plan: 'pro'
});
// Track a goal
window.zenovay('goal', 'purchase', {
value: 99.99
});
// Track revenue
window.zenovay('revenue', 149.99, 'USD', {
order_id: 'ORD-001'
});
Wait for Script Load
function waitForZenovay(callback, maxWait = 5000) {
const startTime = Date.now();
function check() {
if (window.zenovay) {
callback(window.zenovay);
} else if (Date.now() - startTime < maxWait) {
requestAnimationFrame(check);
}
}
check();
}
// Usage
waitForZenovay((zenovay) => {
zenovay('track', 'app_ready');
});
Event Queue Pattern
// Queue function (works before script loads)
window.zenovay = window.zenovay || function() {
(window.zenovay.q = window.zenovay.q || []).push(arguments);
};
// Now you can call zenovay() immediately, even before the script loads
window.zenovay('track', 'early_event', { source: 'inline' });
window.zenovay('page');
Svelte Integration
Setup in App.svelte
<script>
import { onMount } from 'svelte';
import { page } from '$app/stores';
onMount(() => {
// Track initial page view
if (window.zenovay) {
window.zenovay('page');
}
});
// Track route changes
$: if ($page && window.zenovay) {
window.zenovay('page');
}
</script>
Svelte Store
// stores/analytics.js
import { writable } from 'svelte/store';
function createAnalyticsStore() {
const { subscribe } = writable(null);
return {
subscribe,
track: (event, data) => {
if (typeof window !== 'undefined' && window.zenovay) {
window.zenovay('track', event, data);
}
},
identify: (userId, properties) => {
if (typeof window !== 'undefined' && window.zenovay) {
window.zenovay('identify', userId, properties);
}
},
trackGoal: (name, value) => {
if (typeof window !== 'undefined' && window.zenovay) {
window.zenovay('goal', name, { value });
}
}
};
}
export const analytics = createAnalyticsStore();
Svelte Component Usage
<script>
import { analytics } from '../stores/analytics';
function handleClick() {
analytics.track('button_click', { button: 'cta' });
}
</script>
<button on:click={handleClick}>
Click Me
</button>
SvelteKit Integration
// hooks.client.js
import { page } from '$app/stores';
page.subscribe(($page) => {
if (typeof window !== 'undefined' && window.zenovay && $page) {
window.zenovay('page');
}
});
Angular Integration
Service Creation
// analytics.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
declare global {
interface Window {
zenovay: {
(command: 'track', event: string, data?: Record<string, any>): void;
(command: 'page'): void;
(command: 'goal', name: string, data?: Record<string, any>): void;
(command: 'revenue', amount: number, currency: string, meta?: Record<string, any>): void;
(command: 'identify', userId: string, properties?: Record<string, any>): void;
};
}
}
@Injectable({
providedIn: 'root'
})
export class AnalyticsService {
constructor(private router: Router) {
this.setupRouteTracking();
}
private setupRouteTracking() {
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe((event: NavigationEnd) => {
this.trackPageview(event.urlAfterRedirects);
});
}
track(event: string, data?: Record<string, any>) {
if (window.zenovay) {
(window as any).zenovay('track', event, data);
}
}
trackPageview() {
if (window.zenovay) {
(window as any).zenovay('page');
}
}
trackGoal(name: string, value?: number) {
if (window.zenovay) {
(window as any).zenovay('goal', name, { value });
}
}
identify(userId: string, properties?: Record<string, any>) {
if (window.zenovay) {
(window as any).zenovay('identify', userId, properties);
}
}
}
Component Usage
// signup.component.ts
import { Component } from '@angular/core';
import { AnalyticsService } from './analytics.service';
@Component({
selector: 'app-signup',
template: `<button (click)="handleSignup()">Sign Up</button>`
})
export class SignupComponent {
constructor(private analytics: AnalyticsService) {}
handleSignup() {
this.analytics.track('signup_click', {
source: 'header'
});
}
}
Module Setup
// app.module.ts
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { AnalyticsService } from './analytics.service';
export function initAnalytics(analytics: AnalyticsService) {
return () => {
// Initialize tracking
};
}
@NgModule({
providers: [
AnalyticsService,
{
provide: APP_INITIALIZER,
useFactory: initAnalytics,
deps: [AnalyticsService],
multi: true
}
]
})
export class AppModule {}
Ember.js Integration
Service Creation
// app/services/analytics.js
import Service from '@ember/service';
import { inject as service } from '@ember/service';
export default class AnalyticsService extends Service {
@service router;
constructor() {
super(...arguments);
this.setupRouteTracking();
}
setupRouteTracking() {
this.router.on('routeDidChange', () => {
this.trackPageview(this.router.currentURL);
});
}
track(event, data) {
if (window.zenovay) {
window.zenovay('track', event, data);
}
}
trackPageview() {
if (window.zenovay) {
window.zenovay('page');
}
}
identify(userId, properties) {
if (window.zenovay) {
window.zenovay('identify', userId, properties);
}
}
}
Alpine.js Integration
<div x-data="{ analytics: $store.analytics }">
<button @click="analytics.track('click', { button: 'cta' })">
Click Me
</button>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('analytics', {
track(event, data) {
if (window.zenovay) {
window.zenovay('track', event, data);
}
},
identify(userId, properties) {
if (window.zenovay) {
window.zenovay('identify', userId, properties);
}
}
});
});
</script>
jQuery Integration
// Initialize tracking helper
$.zenovay = {
track: function(event, data) {
if (window.zenovay) {
window.zenovay('track', event, data);
}
},
identify: function(userId, properties) {
if (window.zenovay) {
window.zenovay('identify', userId, properties);
}
}
};
// Usage
$(document).ready(function() {
$('#signup-btn').click(function() {
$.zenovay.track('signup_click', {
button: $(this).data('name')
});
});
});
Web Components
// analytics-tracker.js
class AnalyticsTracker extends HTMLElement {
connectedCallback() {
this.addEventListener('click', this.handleClick.bind(this));
}
handleClick() {
const event = this.getAttribute('data-event');
const dataAttrs = {};
for (const attr of this.attributes) {
if (attr.name.startsWith('data-prop-')) {
const key = attr.name.replace('data-prop-', '');
dataAttrs[key] = attr.value;
}
}
if (window.zenovay && event) {
window.zenovay('track', event, dataAttrs);
}
}
}
customElements.define('analytics-tracker', AnalyticsTracker);
Usage:
<analytics-tracker
data-event="cta_click"
data-prop-button="hero"
data-prop-page="home"
>
<button>Get Started</button>
</analytics-tracker>
Single Page Application (SPA) Patterns
History API Tracking
// Track history changes
(function() {
const pushState = history.pushState;
const replaceState = history.replaceState;
history.pushState = function() {
pushState.apply(history, arguments);
trackPageChange();
};
history.replaceState = function() {
replaceState.apply(history, arguments);
trackPageChange();
};
window.addEventListener('popstate', trackPageChange);
function trackPageChange() {
if (window.zenovay) {
// Small delay to ensure DOM updated
setTimeout(() => {
window.zenovay('page');
}, 100);
}
}
})();
Hash-Based Routing
window.addEventListener('hashchange', function() {
if (window.zenovay) {
window.zenovay('page');
}
});
TypeScript Definitions
// types/zenovay.d.ts
interface ZenovayEventData {
[key: string]: string | number | boolean | undefined;
}
interface ZenovayRevenueData {
order_id: string;
value: number;
currency?: string;
items?: Array<{
id: string;
name: string;
price: number;
quantity: number;
}>;
}
interface Zenovay {
(command: 'track', event: string, data?: ZenovayEventData): void;
(command: 'page'): void;
(command: 'goal', name: string, data?: { value?: number }): void;
(command: 'revenue', amount: number, currency: string, meta?: Record<string, unknown>): void;
(command: 'identify', userId: string, properties?: ZenovayEventData): void;
}
declare global {
interface Window {
zenovay?: Zenovay;
}
}
export {};
Wrapper Library Pattern
Create a reusable wrapper:
// lib/analytics.ts
class Analytics {
private queue: Array<() => void> = [];
private ready = false;
constructor() {
this.waitForScript();
}
private waitForScript() {
const check = () => {
if (window.zenovay) {
this.ready = true;
this.processQueue();
} else {
requestAnimationFrame(check);
}
};
check();
}
private processQueue() {
this.queue.forEach(fn => fn());
this.queue = [];
}
private execute(fn: () => void) {
if (this.ready) {
fn();
} else {
this.queue.push(fn);
}
}
track(event: string, data?: Record<string, any>) {
this.execute(() => (window.zenovay as any)('track', event, data));
}
pageview() {
this.execute(() => (window.zenovay as any)('page'));
}
identify(userId: string, properties?: Record<string, any>) {
this.execute(() => (window.zenovay as any)('identify', userId, properties));
}
goal(name: string, value?: number) {
this.execute(() => (window.zenovay as any)('goal', name, { value }));
}
revenue(amount: number, currency: string, meta?: Record<string, any>) {
this.execute(() => (window.zenovay as any)('revenue', amount, currency, meta));
}
}
export const analytics = new Analytics();
Testing Your Integration
Debug Mode
// Enable debug mode
localStorage.setItem('zenovay_debug', 'true');
// View events in console
// Reload page - events will be logged
Verification Steps
- Open browser DevTools
- Go to Network tab
- Filter by "zenovay" or "analytics"
- Perform actions
- Verify requests sent
Test Event
// Quick test
if (window.zenovay) {
window.zenovay('track', 'test_event', {
test: true,
timestamp: Date.now()
});
console.log('Test event sent');
} else {
console.error('Zenovay not loaded');
}
Troubleshooting
Script Not Loading
Check:
- Script URL correct
- No ad blocker interference
- Website ID valid
- Network requests visible
Events Not Tracking
Verify:
window.zenovayexists- Data format correct
- No JavaScript errors
- Domain allowed
SPA Navigation Issues
Ensure:
- Route changes detected
trackPageviewcalled on navigation- No duplicate tracking