The @sentry/nuxt package provides comprehensive error tracking and performance monitoring for Nuxt applications, with support for both client and server environments.
Prerequisites
- Node.js 18.19.1 or newer
- Nuxt 3.7.0 or newer (3.14.0+ recommended) or Nuxt 4.x
- A Sentry account and project DSN
Nuxt 3.14.0+ is recommended for the best experience. Older versions are supported but may have limitations.
Installation
Install the Package
Install @sentry/nuxt using your preferred package manager:Current Version: 10.42.0 Register the Sentry Module
Add the Sentry module to your nuxt.config.ts:// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@sentry/nuxt/module'],
sentry: {
// Optional: Sentry configuration
dsn: 'YOUR_DSN_HERE',
},
});
Create Client Configuration
Create sentry.client.config.ts in your project root:import * as Sentry from '@sentry/nuxt';
Sentry.init({
dsn: 'YOUR_DSN_HERE',
integrations: [
Sentry.replayIntegration(),
],
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
Create Server Configuration
Create sentry.server.config.ts in your project root:import * as Sentry from '@sentry/nuxt';
Sentry.init({
dsn: 'YOUR_DSN_HERE',
tracesSampleRate: 1.0,
});
Verify Installation
Create a test page to verify Sentry is working:<!-- pages/sentry-test.vue -->
<template>
<button @click="triggerError">
Trigger Test Error
</button>
</template>
<script setup>
import * as Sentry from '@sentry/nuxt';
function triggerError() {
Sentry.captureException(new Error('Test error'));
}
</script>
Visit the page and click the button. Check your Sentry dashboard to see the error.
Configuration Options
Configure Sentry in your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@sentry/nuxt/module'],
sentry: {
// Sentry DSN
dsn: process.env.SENTRY_DSN,
// Enable source maps upload
sourceMapsUploadOptions: {
org: 'your-org',
project: 'your-project',
authToken: process.env.SENTRY_AUTH_TOKEN,
},
// Enable debugging in development
debug: process.env.NODE_ENV === 'development',
},
});
Error Handling
Automatic Error Capture
The SDK automatically captures:
- Unhandled exceptions on client and server
- Vue errors and warnings
- API errors
- Server middleware errors
Manual Error Capture
In Vue components:
<template>
<div>
<button @click="fetchData">Load Data</button>
</div>
</template>
<script setup>
import * as Sentry from '@sentry/nuxt';
const fetchData = async () => {
try {
const data = await $fetch('/api/data');
return data;
} catch (error) {
Sentry.captureException(error);
throw error;
}
};
</script>
In Server API Routes
// server/api/users.ts
import * as Sentry from '@sentry/nuxt';
export default defineEventHandler(async (event) => {
try {
const users = await prisma.user.findMany();
return users;
} catch (error) {
Sentry.captureException(error);
throw createError({
statusCode: 500,
message: 'Failed to fetch users',
});
}
});
In Server Middleware
// server/middleware/auth.ts
import * as Sentry from '@sentry/nuxt';
export default defineEventHandler((event) => {
try {
const token = getCookie(event, 'auth_token');
if (!token) {
throw new Error('No auth token');
}
// Verify token
const user = verifyToken(token);
event.context.user = user;
} catch (error) {
Sentry.captureException(error);
throw createError({
statusCode: 401,
message: 'Unauthorized',
});
}
});
Automatic Instrumentation
The SDK automatically creates spans for:
- Page loads and navigation
- API requests
- Server routes
- Database queries (with integrations)
Custom Spans
In components:
<script setup>
import * as Sentry from '@sentry/nuxt';
const loadData = async () => {
const data = await Sentry.startSpan(
{ name: 'fetch-user-data', op: 'http.client' },
async () => {
return await $fetch('/api/users');
}
);
return data;
};
</script>
In server routes:
// server/api/products.ts
import * as Sentry from '@sentry/nuxt';
export default defineEventHandler(async (event) => {
const products = await Sentry.startSpan(
{ name: 'fetch-products', op: 'db.query' },
async () => {
return await prisma.product.findMany();
}
);
return products;
});
Setting Context
User Context
// composables/useAuth.ts
import * as Sentry from '@sentry/nuxt';
export function useAuth() {
const user = useState('user');
watch(user, (newUser) => {
if (newUser) {
Sentry.setUser({
id: newUser.id,
email: newUser.email,
username: newUser.username,
});
} else {
Sentry.setUser(null);
}
}, { immediate: true });
return { user };
}
Tags and Extra Context
import * as Sentry from '@sentry/nuxt';
// Set tags
Sentry.setTag('locale', useRoute().params.locale);
Sentry.setTag('user_tier', 'premium');
// Set extra context
Sentry.setExtra('api_version', 'v2');
Sentry.setContext('app', {
name: 'My App',
version: '1.0.0',
});
Breadcrumbs
import * as Sentry from '@sentry/nuxt';
Sentry.addBreadcrumb({
category: 'navigation',
message: 'User navigated to products page',
level: 'info',
data: {
from: previousRoute.path,
to: currentRoute.path,
},
});
Composables
useSentry Composable
Create a custom composable for easier Sentry usage:
// composables/useSentry.ts
import * as Sentry from '@sentry/nuxt';
export function useSentry() {
const captureError = (error: Error, context?: Record<string, any>) => {
if (context) {
Sentry.withScope((scope) => {
Object.entries(context).forEach(([key, value]) => {
scope.setContext(key, value);
});
Sentry.captureException(error);
});
} else {
Sentry.captureException(error);
}
};
const captureMessage = (message: string, level: Sentry.SeverityLevel = 'info') => {
Sentry.captureMessage(message, level);
};
const addBreadcrumb = (breadcrumb: Sentry.Breadcrumb) => {
Sentry.addBreadcrumb(breadcrumb);
};
return {
captureError,
captureMessage,
addBreadcrumb,
};
}
Use it in components:
<script setup>
const { captureError, addBreadcrumb } = useSentry();
const submitForm = async () => {
addBreadcrumb({
category: 'form',
message: 'User submitted contact form',
});
try {
await $fetch('/api/contact', {
method: 'POST',
body: formData,
});
} catch (error) {
captureError(error, {
form: 'contact',
data: formData,
});
}
};
</script>
Session Replay
Configure Session Replay in your client config:
// sentry.client.config.ts
import * as Sentry from '@sentry/nuxt';
Sentry.init({
dsn: 'YOUR_DSN_HERE',
integrations: [
Sentry.replayIntegration({
maskAllText: true,
blockAllMedia: true,
maskAllInputs: true,
}),
],
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
Pinia Integration
Track Pinia store actions:
// stores/user.ts
import { defineStore } from 'pinia';
import * as Sentry from '@sentry/nuxt';
export const useUserStore = defineStore('user', () => {
const user = ref(null);
const fetchUser = async () => {
Sentry.addBreadcrumb({
category: 'store',
message: 'Fetching user data',
});
try {
const data = await $fetch('/api/user');
user.value = data;
} catch (error) {
Sentry.captureException(error);
throw error;
}
};
return {
user,
fetchUser,
};
});
Source Maps
Source maps are automatically uploaded when configured in nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@sentry/nuxt/module'],
sentry: {
sourceMapsUploadOptions: {
org: 'your-org',
project: 'your-project',
authToken: process.env.SENTRY_AUTH_TOKEN,
// Upload only in production
enabled: process.env.NODE_ENV === 'production',
// Clean up artifacts after upload
cleanArtifacts: true,
},
},
});
Advanced Configuration
Environment Detection
// sentry.client.config.ts
import * as Sentry from '@sentry/nuxt';
Sentry.init({
dsn: 'YOUR_DSN_HERE',
environment: process.env.NODE_ENV,
enabled: process.env.NODE_ENV === 'production',
});
Sampling
Sentry.init({
dsn: 'YOUR_DSN_HERE',
tracesSampler: (samplingContext) => {
// Don't sample health checks
if (samplingContext.request?.url?.includes('/health')) {
return 0;
}
// Sample API routes at 50%
if (samplingContext.request?.url?.startsWith('/api')) {
return 0.5;
}
return 0.1;
},
});
Filtering Events
Sentry.init({
dsn: 'YOUR_DSN_HERE',
beforeSend(event, hint) {
// Don't send errors from development
if (process.env.NODE_ENV === 'development') {
return null;
}
return event;
},
});
Runtime Configuration
Use runtime config for dynamic DSN:
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
sentryDsn: process.env.SENTRY_DSN,
},
},
});
Then in your Sentry config:
// sentry.client.config.ts
import * as Sentry from '@sentry/nuxt';
const config = useRuntimeConfig();
Sentry.init({
dsn: config.public.sentryDsn,
// ... other options
});
Troubleshooting
Errors Not Captured
- Ensure the Sentry module is registered in
nuxt.config.ts
- Check that both
sentry.client.config.ts and sentry.server.config.ts exist
- Verify DSN is correct in both configs
Source Maps Not Working
- Check
SENTRY_AUTH_TOKEN is set correctly
- Verify
org and project match your Sentry organization
- Ensure
sourceMapsUploadOptions is configured
- Ensure
tracesSampleRate is greater than 0
- Check that Sentry module is loaded before your app
- Verify Sentry is initialized in both client and server configs
Make sure to create both sentry.client.config.ts and sentry.server.config.ts in your project root, not in a subdirectory.
Module Not Loading
If the Sentry module isn’t loading:
- Update to the latest
@sentry/nuxt version
- Clear
.nuxt directory: rm -rf .nuxt
- Reinstall dependencies
- Check Nuxt version compatibility
Next Steps