Skip to main content
Zenovay
Free15 minutesIntermediate

Vue.js Integration

Integrate Zenovay with Vue.js applications - Vue 3, Nuxt, and composition API support. Learn about vue in this API integrations guide.

vuenuxtjavascriptintegrationspa
Last updated:

Integrate Zenovay analytics into your Vue.js or Nuxt application using the tracking script tag and the global window.zenovay function.

Installation

Add the Zenovay tracking script to your index.html:

<!-- index.html -->
<script
  defer
  data-tracking-code="YOUR_TRACKING_CODE"
  src="https://api.zenovay.com/z.js"
></script>

No npm package is needed. The script automatically tracks page views and provides the global window.zenovay function for custom events.

Vue 3 Setup

Composable Wrapper

Create a composable to wrap the global window.zenovay function for use in your Vue components:

// composables/useZenovay.js
export function useZenovay() {
  const trackEvent = (name, properties) => {
    if (window.zenovay) {
      window.zenovay('track', name, properties);
    }
  };

  const trackGoal = (name, properties) => {
    if (window.zenovay) {
      window.zenovay('goal', name, properties);
    }
  };

  const trackPageview = () => {
    if (window.zenovay) {
      window.zenovay('page');
    }
  };

  const trackRevenue = (amount, currency, meta) => {
    if (window.zenovay) {
      window.zenovay('revenue', amount, currency, meta);
    }
  };

  const identify = (userId, properties) => {
    if (window.zenovay) {
      window.zenovay('identify', userId, properties);
    }
  };

  return { trackEvent, trackGoal, trackPageview, trackRevenue, identify };
}

Available Functions

const {
  trackEvent,    // Track custom events
  trackGoal,     // Track goal conversions
  trackPageview, // Manual page view
  trackRevenue,  // Track revenue
  identify       // Identify users
} = useZenovay();

Event Tracking

Basic Events

<script setup>
import { useZenovay } from '@/composables/useZenovay';

const { trackEvent } = useZenovay();

const handleSignup = () => {
  trackEvent('signup_click', {
    plan: 'pro',
    source: 'pricing'
  });
};
</script>

<template>
  <button @click="handleSignup">
    Start Free Trial
  </button>
</template>

Component Events

<script setup>
import { useZenovay } from '@/composables/useZenovay';

const { trackEvent } = useZenovay();

const props = defineProps(['product']);

const addToCart = () => {
  trackEvent('add_to_cart', {
    product_id: props.product.id,
    product_name: props.product.name,
    price: props.product.price
  });

  // Continue with cart logic
  emit('add', props.product);
};
</script>

<template>
  <button @click="addToCart">Add to Cart</button>
</template>

Goal Tracking

<script setup>
import { useZenovay } from '@/composables/useZenovay';
import { onMounted } from 'vue';

const props = defineProps(['order']);
const { trackGoal } = useZenovay();

onMounted(() => {
  trackGoal('purchase', {
    value: props.order.total,
    order_id: props.order.id
  });
});
</script>

User Identification

<script setup>
import { useZenovay } from '@/composables/useZenovay';
import { watch } from 'vue';
import { useUserStore } from '@/stores/user';

const userStore = useUserStore();
const { identify } = useZenovay();

watch(
  () => userStore.user,
  (user) => {
    if (user) {
      identify(user.id, {
        email: user.email,
        name: user.name,
        plan: user.subscription
      });
    }
  },
  { immediate: true }
);
</script>

Revenue Tracking

<script setup>
import { useZenovay } from '@/composables/useZenovay';
import { onMounted } from 'vue';

const props = defineProps(['order']);
const { trackRevenue } = useZenovay();

onMounted(() => {
  trackRevenue(props.order.total, 'USD', {
    order_id: props.order.id,
    items: props.order.items.map(item => ({
      id: item.sku,
      name: item.name,
      price: item.price,
      quantity: item.quantity
    }))
  });
});
</script>

<template>
  <div>
    <h1>Order Confirmed!</h1>
    <p>Order #{{ order.id }}</p>
  </div>
</template>

Nuxt 3 Integration

Add Script in nuxt.config.ts

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: 'https://api.zenovay.com/z.js',
          defer: true,
          'data-tracking-code': 'YOUR_TRACKING_CODE'
        }
      ]
    }
  }
});

Environment Variables

# .env
NUXT_PUBLIC_ZENOVAY_TRACKING_CODE=your-tracking-code
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: 'https://api.zenovay.com/z.js',
          defer: true,
          'data-tracking-code': process.env.NUXT_PUBLIC_ZENOVAY_TRACKING_CODE
        }
      ]
    }
  }
});

Nuxt Composable

<script setup>
// Use the same composable from above
import { useZenovay } from '@/composables/useZenovay';

const { trackEvent } = useZenovay();

const handleClick = () => {
  trackEvent('cta_click', { location: 'hero' });
};
</script>

Vue Router Integration

Automatic Tracking

The Zenovay script automatically tracks page views. For SPA route changes, add a router afterEach hook:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),
  routes: [/* your routes */]
});

// Track page views on route changes
router.afterEach((to) => {
  if (window.zenovay) {
    window.zenovay('page');
  }
});

export default router;

Page views tracked automatically on:

  • Initial load
  • Route changes
  • Hash changes (optional)

Manual Route Tracking

<script setup>
import { useZenovay } from '@/composables/useZenovay';
import { useRouter } from 'vue-router';
import { watch } from 'vue';

const router = useRouter();
const { trackPageview } = useZenovay();

watch(
  () => router.currentRoute.value.path,
  (path) => {
    trackPageview(path);
  }
);
</script>

Directive

v-track Directive

<template>
  <!-- Track clicks -->
  <button v-track:click="{ event: 'signup_click', plan: 'pro' }">
    Sign Up
  </button>

  <!-- Track visibility -->
  <div v-track:visible="{ event: 'section_viewed', section: 'pricing' }">
    Pricing Section
  </div>
</template>

Register Directive

// main.js
import { createApp } from 'vue';

const app = createApp(App);

// Custom v-track directive using window.zenovay
app.directive('track', {
  mounted(el, binding) {
    const { event, ...properties } = binding.value;
    const eventType = binding.arg || 'click';

    if (eventType === 'click') {
      el.addEventListener('click', () => {
        if (window.zenovay) {
          window.zenovay('track', event, properties);
        }
      });
    } else if (eventType === 'visible') {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            if (window.zenovay) {
              window.zenovay('track', event, properties);
            }
            observer.unobserve(el);
          }
        });
      });
      observer.observe(el);
    }
  }
});

Options API

Using in Options API

<script>
export default {
  methods: {
    handleClick() {
      if (window.zenovay) {
        window.zenovay('track', 'button_click', {
          button: 'signup'
        });
      }
    }
  }
};
</script>

Pinia Integration

Store with Analytics

// stores/cart.js
import { defineStore } from 'pinia';

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: []
  }),

  actions: {
    addItem(product) {
      this.items.push(product);

      // Track using global zenovay object
      if (window.zenovay) {
        window.zenovay('track', 'add_to_cart', {
          product_id: product.id,
          price: product.price
        });
      }
    }
  }
});

TypeScript Support

Types

// types/zenovay.d.ts
interface ZenovayFunction {
  (command: 'track', name: string, properties?: Record<string, unknown>): void;
  (command: 'identify', userId: string, traits?: Record<string, unknown>): void;
  (command: 'goal', name: string, properties?: Record<string, unknown>): void;
  (command: 'page'): void;
  (command: 'revenue', amount: number, currency: string, meta?: Record<string, unknown>): void;
}

declare global {
  interface Window {
    zenovay: ZenovayFunction;
  }
}

export {};

Usage with Types

<script setup lang="ts">
import { useZenovay } from '@/composables/useZenovay';

interface PurchaseEvent {
  product_id: string;
  price: number;
  quantity: number;
}

const { trackEvent } = useZenovay();

const handlePurchase = (data: PurchaseEvent) => {
  trackEvent('purchase', data);
};
</script>

Testing

Mock Composable

// tests/mocks/zenovay.js
import { vi } from 'vitest';

export const mockZenovay = {
  trackEvent: vi.fn(),
  trackGoal: vi.fn(),
  identify: vi.fn(),
  trackPageview: vi.fn()
};

vi.mock('@/composables/useZenovay', () => ({
  useZenovay: () => mockZenovay
}));

Test Example

import { mount } from '@vue/test-utils';
import { mockZenovay } from './mocks/zenovay';
import SignupButton from '@/components/SignupButton.vue';

test('tracks signup click', async () => {
  const wrapper = mount(SignupButton);

  await wrapper.find('button').trigger('click');

  expect(mockZenovay.trackEvent).toHaveBeenCalledWith(
    'signup_click',
    expect.any(Object)
  );
});

Troubleshooting

Events Not Tracking

Check:

  • Script tag is in your index.html
  • Tracking code is valid
  • Not blocked by ad blocker
  • Console for errors

Router Tracking Not Working

Ensure:

  • Router afterEach hook is configured
  • window.zenovay is available when route changes

SSR Issues

For Nuxt/SSR:

if (process.client) {
  if (window.zenovay) {
    window.zenovay('track', 'client_only');
  }
}

Next Steps

Was this article helpful?