Hero
Back to all guides

How to add analytics to Nuxt

Add privacy-first analytics to your Nuxt app in under 5 minutes with OpenPanel's official Nuxt module.

OpenPanel Team

1/7/2025

Beginner
5 min

How to add analytics to Nuxt

This guide walks you through adding OpenPanel to a Nuxt 3 application. The official @openpanel/nuxt module makes integration effortless with auto-imported composables, automatic page view tracking, and a built-in proxy option to bypass ad blockers.

OpenPanel is an open-source alternative to Mixpanel and Google Analytics. It uses cookieless tracking by default, so you won't need cookie consent banners for basic analytics.

Prerequisites

  • A Nuxt 3 project
  • An OpenPanel account (sign up free)
  • Your Client ID from the OpenPanel dashboard

Install the module

Start by installing the OpenPanel Nuxt module. This package includes everything you need for client-side tracking, including the auto-imported useOpenPanel composable.

npm install @openpanel/nuxt

If you prefer pnpm or yarn, those work too.

Configure the module

Add the module to your nuxt.config.ts and configure it with your Client ID. The module automatically sets up page view tracking and makes the useOpenPanel composable available throughout your app.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@openpanel/nuxt'],
  openpanel: {
    clientId: 'your-client-id',
    trackScreenViews: true,
    trackOutgoingLinks: true,
    trackAttributes: true,
  },
});

That's it. Page views are now being tracked automatically as users navigate your app.

Configuration options

OptionDefaultDescription
clientIdYour OpenPanel client ID (required)
apiUrlhttps://api.openpanel.devThe API URL to send events to
trackScreenViewstrueAutomatically track page views
trackOutgoingLinkstrueTrack clicks on external links
trackAttributestrueTrack elements with data-track attributes
trackHashChangesfalseTrack hash changes in URL
disabledfalseDisable all tracking
proxyfalseRoute events through your server

Using environment variables

For production applications, store your Client ID in environment variables.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@openpanel/nuxt'],
  openpanel: {
    clientId: process.env.NUXT_PUBLIC_OPENPANEL_CLIENT_ID,
    trackScreenViews: true,
  },
});
.env
NUXT_PUBLIC_OPENPANEL_CLIENT_ID=your-client-id

Track custom events

Page views only tell part of the story. To understand how users interact with your product, track custom events like button clicks, form submissions, or feature usage.

Using the composable

The useOpenPanel composable is auto-imported, so you can use it directly in any component without importing anything.

components/SignupButton.vue
<script setup>
const op = useOpenPanel();

function handleClick() {
  op.track('button_clicked', {
    button_name: 'signup',
    button_location: 'hero',
  });
}
</script>

<template>
  <button type="button" @click="handleClick">Sign Up</button>
</template>

Accessing via useNuxtApp

You can also access the OpenPanel instance through useNuxtApp() if you prefer.

<script setup>
const { $openpanel } = useNuxtApp();

$openpanel.track('my_event', { foo: 'bar' });
</script>

Track form submissions

Form tracking helps you understand conversion rates and identify where users drop off.

components/ContactForm.vue
<script setup>
const op = useOpenPanel();
const email = ref('');

async function handleSubmit() {
  op.track('form_submitted', {
    form_name: 'contact',
    form_location: 'homepage',
  });
  
  // Your form submission logic
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="email" type="email" placeholder="Enter your email" />
    <button type="submit">Submit</button>
  </form>
</template>

Use data attributes for declarative tracking

The SDK supports declarative tracking using data-track attributes. This is useful for simple click tracking without writing JavaScript.

<template>
  <button
    data-track="button_clicked"
    data-track-button_name="signup"
    data-track-button_location="hero"
  >
    Sign Up
  </button>
</template>

When a user clicks this button, OpenPanel automatically sends a button_clicked event with the specified properties. This requires trackAttributes: true in your configuration.

Identify users

Anonymous tracking is useful, but identifying users unlocks more valuable insights. You can track behavior across sessions, segment users by properties, and build cohort analyses.

Call identify after a user logs in or when you have their information available.

components/UserProfile.vue
<script setup>
const op = useOpenPanel();
const props = defineProps(['user']);

watch(() => props.user, (user) => {
  if (user) {
    op.identify({
      profileId: user.id,
      firstName: user.firstName,
      lastName: user.lastName,
      email: user.email,
      properties: {
        plan: user.plan,
        signupDate: user.createdAt,
      },
    });
  }
}, { immediate: true });
</script>

<template>
  <div>Welcome, {{ user?.firstName }}!</div>
</template>

Set global properties

Properties set with setGlobalProperties are included with every event. This is useful for app version tracking, feature flags, or A/B test variants.

app.vue
<script setup>
const op = useOpenPanel();

onMounted(() => {
  op.setGlobalProperties({
    app_version: '1.0.0',
    environment: useRuntimeConfig().public.environment,
  });
});
</script>

Clear user data on logout

When users log out, clear the stored profile data to ensure subsequent events aren't associated with the previous user.

components/LogoutButton.vue
<script setup>
const op = useOpenPanel();

function handleLogout() {
  op.clear();
  navigateTo('/login');
}
</script>

<template>
  <button @click="handleLogout">Logout</button>
</template>

Set up server-side tracking

For tracking events in server routes, API endpoints, or server middleware, use the @openpanel/sdk package. Server-side tracking requires a client secret for authentication.

Install the SDK

npm install @openpanel/sdk

Create a server instance

server/utils/op.ts
import { OpenPanel } from '@openpanel/sdk';

export const op = new OpenPanel({
  clientId: process.env.OPENPANEL_CLIENT_ID!,
  clientSecret: process.env.OPENPANEL_CLIENT_SECRET!,
});

Track events in server routes

server/api/webhook.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event);
  
  await op.track('webhook_received', {
    source: body.source,
    event_type: body.type,
  });
  
  return { success: true };
});

Never expose your client secret on the client side. Keep it in server-only code.

Awaiting in serverless environments

If you're deploying to a serverless platform like Vercel or Netlify, make sure to await the tracking call to ensure it completes before the function terminates.

export default defineEventHandler(async (event) => {
  // Always await in serverless environments
  await op.track('my_server_event', { foo: 'bar' });
  return { message: 'Event logged!' };
});

Bypass ad blockers with proxy

Many ad blockers block requests to third-party analytics domains. The Nuxt module includes a built-in proxy that routes events through your own server.

Enable the proxy option in your configuration:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@openpanel/nuxt'],
  openpanel: {
    clientId: 'your-client-id',
    proxy: true, // Routes events through /api/openpanel/*
  },
});

When proxy: true is set:

  • The module automatically sets apiUrl to /api/openpanel
  • A server handler is registered at /api/openpanel/**
  • All tracking requests route through your Nuxt server

This makes tracking requests invisible to browser extensions that block third-party analytics.

Verify your setup

Open your Nuxt app in the browser and navigate between a few pages. Interact with elements that trigger custom events. Then open your OpenPanel dashboard and check the Real-time view to see events appearing within seconds.

If events aren't showing up, check the browser console for errors. The most common issues are:

  • Incorrect client ID
  • Ad blockers intercepting requests (enable the proxy option)
  • Client ID exposed in server-only code

The Network tab in your browser's developer tools can help you confirm that requests are being sent.

Next steps

The Nuxt SDK reference covers additional features like incrementing user properties and event filtering. If you're interested in understanding how OpenPanel handles privacy, read our article on cookieless analytics.

Loved by builders everywhere

From indie hackers to global teams, OpenPanel helps people understand their users effortlessly.

  • Steven Tey
    Steven Tey
    @steventey

    Open-source Mixpanel alternative just dropped → http://git.new/openpanel

    It combines the power of Mixpanel + the ease of use of @PlausibleHQ into a fully open-source product.

    Built by @CarlLindesvard and it’s already tracking 750K+ events 🤩

  • Pontus Abrahamsson - oss/acc
    Pontus Abrahamsson - oss/acc
    @pontusab

    Thanks, OpenPanel is a beast, love it!

  • Piotr Kulpinski
    Piotr Kulpinski
    @piotrkulpinski

    The Overview tab in OpenPanel is great. It has everything I need from my analytics: the stats, the graph, traffic sources, locations, devices, etc.

    The UI is beautiful ✨ Clean, modern look, very pleasing to the eye.

  • greg hodson 🍜
    greg hodson 🍜
    @h0dson

    i second this, openpanel is killing it

  • Jacob 🍀 Build in Public
    Jacob 🍀 Build in Public
    @javayhuwx

    🤯 wow, it's amazing! Just integrate @OpenPanelDev into http://indiehackers.site last night, and now I can see visitors coming from all round the world.

    OpenPanel has a more beautiful UI and much more powerful features when compared to Umami.

    #buildinpublic #indiehackers

  • Lee
    Lee
    @DutchEngIishman

    Day two of marketing.

    I like this upward trend..

    P.S. website went live on Sunday

    P.P.S. Openpanel by @CarlLindesvard is awesome.

Ready to understand your users better?
Start tracking in minutes

Join thousands of companies using OpenPanel. Free 30-day trial, no credit card required. Self-host for free or use our cloud.

Get started now