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/nuxtIf 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.
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
| Option | Default | Description |
|---|---|---|
clientId | — | Your OpenPanel client ID (required) |
apiUrl | https://api.openpanel.dev | The API URL to send events to |
trackScreenViews | true | Automatically track page views |
trackOutgoingLinks | true | Track clicks on external links |
trackAttributes | true | Track elements with data-track attributes |
trackHashChanges | false | Track hash changes in URL |
disabled | false | Disable all tracking |
proxy | false | Route events through your server |
Using environment variables
For production applications, store your Client ID in environment variables.
export default defineNuxtConfig({
modules: ['@openpanel/nuxt'],
openpanel: {
clientId: process.env.NUXT_PUBLIC_OPENPANEL_CLIENT_ID,
trackScreenViews: true,
},
});NUXT_PUBLIC_OPENPANEL_CLIENT_ID=your-client-idTrack 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.
<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.
<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.
<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.
<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.
<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/sdkCreate a server instance
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
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:
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
apiUrlto/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.


