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: '[email protected]',
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
Turn on debug logging to see tracker activity in the browser console. There are three ways to enable it:
<!-- 1. Add the data-debug attribute to the script tag -->
<script
defer
data-tracking-code="YOUR_TRACKING_CODE"
data-debug="true"
src="https://api.zenovay.com/z.js"
></script>
2. Append ?zenovay_debug=true to any page URL (handy for production).
3. Debug logging is on automatically when running on localhost.
With debug enabled, the tracker logs each event it sends to the console.
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