debug:nuxtjs
Nuxt.js Debugging Guide
This guide provides a systematic approach to debugging Nuxt.js applications, covering SSR/SSG issues, Nitro server problems, hydration mismatches, composables, and more.
Common Error Patterns
1. Hydration Mismatches
Hydration mismatches occur when the server-rendered HTML differs from what Vue expects on the client.
Symptoms:
- Console warning: "Hydration text/node mismatch"
- Content flickers or changes after page load
[Vue warn]: Hydration completed but contains mismatches
Common Causes:
// BAD: Using browser-only APIs during SSR
const windowWidth = window.innerWidth // Errors on server
// GOOD: Guard with process.client or useNuxtApp()
const windowWidth = ref(0)
onMounted(() => {
windowWidth.value = window.innerWidth
})
// GOOD: Use ClientOnly component
<ClientOnly>
<BrowserOnlyComponent />
</ClientOnly>
// BAD: Date/time rendering inconsistency
<span>{{ new Date().toLocaleString() }}</span> // Different on server vs client
// GOOD: Use consistent formatting or client-only
<ClientOnly>
<span>{{ formattedDate }}</span>
<template #fallback>Loading...</template>
</ClientOnly>
Debugging Steps:
- Check browser console for specific mismatch details
- Look for
window,document,localStorageusage outsideonMountedorprocess.client - Check for random values, dates, or user-specific data rendered during SSR
- Use Vue DevTools to inspect component tree
2. useFetch/useAsyncData Errors
Common Issues:
// ERROR: "useFetch is not defined" or composable called outside setup
// BAD: Calling in regular function
function fetchData() {
const { data } = useFetch('/api/data') // Error!
}
// GOOD: Call in setup or use $fetch in functions
const { data, error, pending, refresh } = useFetch('/api/data')
// Or for functions:
async function fetchData() {
const data = await $fetch('/api/data')
}
Key/Caching Issues:
// BAD: Same key returns cached data
const { data: user1 } = useFetch('/api/user', { key: 'user' })
const { data: user2 } = useFetch('/api/user', { key: 'user' }) // Returns same cached data!
// GOOD: Use unique keys
const { data: user1 } = useFetch('/api/user/1', { key: 'user-1' })
const { data: user2 } = useFetch('/api/user/2', { key: 'user-2' })
// Force refresh
const { data, refresh } = useFetch('/api/data')
await refresh() // Bypasses cache
Watch for Reactive Parameters:
// BAD: Non-reactive parameter won't trigger refetch
const userId = '123'
const { data } = useFetch(`/api/user/${userId}`)
// GOOD: Use computed or getter for reactive URLs
const userId = ref('123')
const { data } = useFetch(() => `/api/user/${userId.value}`)
// Or with watch
const { data } = useFetch('/api/user', {
query: { id: userId },
watch: [userId]
})
3. Nitro Server Errors
500 Internal Server Errors:
// Check server/api/ files for issues
// server/api/example.ts
// BAD: Unhandled errors crash the endpoint
export default defineEventHandler(async (event) => {
const data = await fetchExternalAPI() // Unhandled rejection
return data
})
// GOOD: Proper error handling
export default defineEventHandler(async (event) => {
try {
const data = await fetchExternalAPI()
return data
} catch (error) {
throw createError({
statusCode: 500,
statusMessage: 'Failed to fetch data',
data: { originalError: error.message }
})
}
})
Reading Request Body:
// BAD: Wrong method to read body
export default defineEventHandler(async (event) => {
const body = event.body // undefined!
// GOOD: Use readBody
const body = await readBody(event)
// For query params
const query = getQuery(event)
// For route params
const { id } = event.context.params
})
4. Plugin Initialization Issues
// plugins/my-plugin.ts
// BAD: Plugin errors break the entire app
export default defineNuxtPlugin(() => {
const api = new ExternalAPI() // May throw
})
// GOOD: Error handling in plugins
export default defineNuxtPlugin({
name: 'my-plugin',
enforce: 'pre', // or 'post'
async setup(nuxtApp) {
try {
const api = new ExternalAPI()
return {
provide: {
api
}
}
} catch (error) {
console.error('Plugin initialization failed:', error)
// Provide fallback or skip
}
}
})
// Client-only plugin
export default defineNuxtPlugin({
name: 'client-only-plugin',
setup() {
// This only runs on client
}
})
// Name file: plugins/my-plugin.client.ts
5. Module Conflicts
Diagnosing Module Issues:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
// Module order can matter!
],
// Debug module loading
debug: true, // Shows module loading in console
})
Common Module Conflicts:
# Clear module cache
rm -rf node_modules/.cache
rm -rf .nuxt
# Reinstall dependencies
rm -rf node_modules
npm install
Debugging Tools
1. Nuxt DevTools (Recommended)
// nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true }
})
Features:
- Component inspector and tree
- Pages and routing visualization
- Composables state inspection
- Server routes overview
- Module dependencies
- Payload inspection
- Timeline for performance
Access: Press Shift + Alt + D or click floating icon in dev mode
2. Sourcemaps Configuration
// nuxt.config.ts
export default defineNuxtConfig({
sourcemap: {
server: true,
client: true
}
})
3. VS Code Debugging
Create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Debug Nuxt Client",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
},
{
"type": "node",
"request": "launch",
"name": "Debug Nuxt Server",
"program": "${workspaceFolder}/node_modules/nuxi/bin/nuxi.mjs",
"args": ["dev"],
"cwd": "${workspaceFolder}"
}
]
}
4. Node Inspector (Server-Side)
# Start with debugger
nuxi dev --inspect
# Or with specific host for Docker
nuxi dev --inspect=0.0.0.0
5. Console Logging (Server vs Client)
// Runs on both server and client
console.log('Universal log')
// Server-only logging
if (process.server) {
console.log('Server-side only')
}
// Client-only logging
if (process.client) {
console.log('Client-side only')
}
// In composables
const nuxtApp = useNuxtApp()
if (nuxtApp.ssrContext) {
console.log('Server-side render')
}
6. Vue DevTools
# Install Vue DevTools browser extension
# Or use standalone
npx @vue/devtools
The Four Phases of Nuxt Debugging
Phase 1: Identify the Context
Determine where the error occurs:
// Check execution context
console.log('Server:', process.server)
console.log('Client:', process.client)
console.log('Dev:', process.dev)
console.log('SSR:', !!useNuxtApp().ssrContext)
Questions to answer:
- Does it happen during SSR, hydration, or client navigation?
- Is it a build-time or runtime error?
- Does it only happen on certain routes?
- Is it reproducible in development AND production?
Phase 2: Isolate the Component
<template>
<div>
<!-- Wrap suspect components -->
<NuxtErrorBoundary @error="logError">
<SuspectComponent />
<template #error="{ error }">
<p>Error: {{ error.message }}</p>
</template>
</NuxtErrorBoundary>
</div>
</template>
<script setup>
function logError(error) {
console.error('Caught error:', error)
}
</script>
Phase 3: Check Data Flow
// Debug useFetch/useAsyncData
const { data, error, pending, status } = useFetch('/api/data')
watch([data, error, pending], ([d, e, p]) => {
console.log('Data:', d)
console.log('Error:', e)
console.log('Pending:', p)
})
// Check payload (what's sent from server to client)
const nuxtApp = useNuxtApp()
console.log('Payload:', nuxtApp.payload)
Phase 4: Verify Build and Config
# Type check
nuxi typecheck
# Analyze bundle
nuxi analyze
# Clean build
rm -rf .nuxt .output node_modules/.cache
nuxi build
Quick Reference Commands
Development
# Start dev server
nuxi dev
# Start with debugging
nuxi dev --inspect
# Start on specific port
nuxi dev --port 3001
# Start with HTTPS
nuxi dev --https
Building and Analysis
# Production build
nuxi build
# Generate static site
nuxi generate
# Preview production build
nuxi preview
# Analyze bundle size
nuxi analyze
# Type checking
nuxi typecheck
# Prepare Nuxt (generate types)
nuxi prepare
Maintenance
# Clean Nuxt files
nuxi cleanup
# Upgrade Nuxt
nuxi upgrade
# Add module
nuxi module add @nuxtjs/tailwindcss
# Create new component/page/etc
nuxi add component MyComponent
nuxi add page about
nuxi add composable useMyComposable
nuxi add api hello
Error Handling Patterns
Global Error Handler
// plugins/error-handler.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
console.error('Vue Error:', error)
console.error('Component:', instance)
console.error('Info:', info)
}
nuxtApp.hook('vue:error', (error, instance, info) => {
console.error('Nuxt Vue Error Hook:', error)
})
nuxtApp.hook('app:error', (error) => {
console.error('App Error:', error)
})
})
Custom Error Page
<!-- error.vue (in root, NOT in pages/) -->
<template>
<div class="error-page">
<h1>{{ error.statusCode }}</h1>
<p>{{ error.message }}</p>
<button @click="handleError">Go Home</button>
</div>
</template>
<script setup>
const props = defineProps({
error: Object
})
const handleError = () => clearError({ redirect: '/' })
</script>
Programmatic Error Handling
// Throw errors
throw createError({
statusCode: 404,
statusMessage: 'Page not found',
fatal: true // Triggers error page
})
// Show error without navigation
showError({
statusCode: 500,
statusMessage: 'Something went wrong'
})
// Clear error
clearError({ redirect: '/' })
// Access current error
const error = useError()
Error Boundary for Components
<template>
<NuxtErrorBoundary>
<RiskyComponent />
<template #error="{ error, clearError }">
<div class="error-box">
<p>Component failed: {{ error.message }}</p>
<button @click="clearError">Retry</button>
</div>
</template>
</NuxtErrorBoundary>
</template>
SSR-Specific Debugging
Payload Issues
// Debug what's in the payload
const nuxtApp = useNuxtApp()
onMounted(() => {
console.log('SSR Payload:', nuxtApp.payload)
console.log('SSR Data:', nuxtApp.payload.data)
console.log('SSR State:', nuxtApp.payload.state)
})
Async Data Not Available
// Ensure data is awaited properly
const { data } = await useFetch('/api/data')
// For lazy loading (doesn't block navigation)
const { data, pending } = useLazyFetch('/api/data')
// Watch for data availability
watch(data, (newData) => {
if (newData) {
console.log('Data loaded:', newData)
}
})
Server-Only Code Leaking to Client
// Use server utilities correctly
// server/utils/db.ts - only available in server/
// For runtime config (secrets)
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
apiSecret: '', // Server-only
public: {
apiBase: '' // Exposed to client
}
}
})
// Usage
const config = useRuntimeConfig()
// config.apiSecret - only on server
// config.public.apiBase - available everywhere
Performance Debugging
Identify Slow Components
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
componentIslands: true // For heavy components
}
})
<!-- Use islands for heavy server components -->
<NuxtIsland name="HeavyChart" :props="{ data: chartData }" />
Lazy Loading
// Lazy load components
const HeavyComponent = defineAsyncComponent(() =>
import('~/components/HeavyComponent.vue')
)
// Or use Nuxt's auto-import with Lazy prefix
<template>
<LazyHeavyComponent v-if="showHeavy" />
</template>
Bundle Analysis
# Generate bundle analysis
nuxi analyze
# Check what's in your bundle
cat .output/public/_nuxt/builds/meta/*.json | jq
Common Gotchas
1. Composables Must Be Called in Setup
// BAD
function handleClick() {
const route = useRoute() // Error!
}
// GOOD
const route = useRoute()
function handleClick() {
console.log(route.path)
}
2. Reactive Data in useFetch
// BAD: Non-reactive
const id = '123'
useFetch(`/api/items/${id}`)
// GOOD: Reactive
const id = ref('123')
useFetch(() => `/api/items/${id.value}`)
3. Navigate vs Router
// Prefer navigateTo over useRouter for navigation
await navigateTo('/dashboard')
await navigateTo({ path: '/user', query: { id: 1 } })
// For programmatic redirects in server
export default defineEventHandler((event) => {
return sendRedirect(event, '/login', 302)
})
4. Middleware Execution Order
// Named middleware runs in alphabetical order
// middleware/01.auth.global.ts runs before middleware/02.analytics.global.ts
// Route-specific middleware
definePageMeta({
middleware: ['auth', 'premium'] // Runs in order
})
5. State Pollution in SSR
// BAD: Shared state between requests
const globalState = reactive({})
// GOOD: Use useState for SSR-safe state
const state = useState('key', () => ({}))
// Or Pinia with proper SSR setup
const store = useMyStore()
Resources
More from snakeo/claude-debug-and-refactor-skills-plugin
refactor:flutter
Refactor Flutter/Dart code to improve maintainability, readability, and performance. This skill applies Dart 3 features like records, patterns, and sealed classes, implements proper state management with Riverpod or BLoC, and uses Freezed for immutable models. It addresses monolithic widgets, missing const constructors, improper BuildContext usage, and deep nesting. Apply when you notice widgets doing too much, performance issues from unnecessary rebuilds, or legacy Dart 2 patterns.
60refactor:nestjs
Refactor NestJS/TypeScript code to improve maintainability, readability, and adherence to best practices. Identifies and fixes circular dependencies, god object services, fat controllers with business logic, deep nesting, and SRP violations. Applies NestJS patterns including proper module organization, provider scopes, custom decorators, guards, interceptors, pipes, DTOs with class-validator, exception filters, CQRS, repository pattern, and event-driven architecture. Transforms code into exemplary implementations following SOLID principles.
42refactor:spring-boot
Refactor Spring Boot and Java code to improve maintainability, readability, and adherence to enterprise best practices. This skill transforms messy Spring Boot applications into clean, well-structured solutions following SOLID principles and Spring Boot 3.x conventions. It addresses fat controllers, improper transaction boundaries, field injection anti-patterns, and scattered configuration. Leverages Java 21+ features including record patterns, pattern matching for switch, virtual threads, and sequenced collections.
39debug:flutter
Debug Flutter applications systematically with this comprehensive troubleshooting skill. Covers RenderFlex overflow errors, setState() after dispose() issues, null check operator failures, platform channel problems, build context errors, and hot reload failures. Provides structured four-phase debugging methodology with Flutter DevTools, widget inspector, performance profiling, and platform-specific debugging for Android, iOS, and web targets.
38refactor:nuxtjs
Refactor Nuxt.js/Vue code to improve maintainability, readability, and adherence to best practices. Identifies and fixes DRY violations, oversized components, deep nesting, SRP violations, data fetching anti-patterns with useFetch/useAsyncData/$fetch, poor composable organization, and mixed business/presentation logic. Applies Nuxt 3 patterns including auto-imports, proper data fetching, single-responsibility composables, TypeScript integration, runtime config, Nitro server routes, Nuxt Layers, middleware patterns, Pinia state management, and performance optimizations.
36refactor:django
Refactor Django/Python code to improve maintainability, readability, and adherence to best practices. Transforms fat views, N+1 queries, and outdated patterns into clean, modern Django code. Applies Python 3.12+ features like type parameter syntax and @override decorator, Django 5+ patterns like GeneratedField and async views, service layer architecture, and PEP 8 conventions. Identifies and fixes anti-patterns including mutable defaults, bare exceptions, and improper ORM usage.
35