| name | debug:nuxtjs |
| description | Debug Nuxt.js issues systematically. Use when encountering SSR errors, Nitro server issues, hydration mismatches like "Hydration text/node mismatch", composable problems with useFetch or useAsyncData, plugin initialization failures, module conflicts, auto-import issues, or Vue-specific runtime errors in a Nuxt context. |
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:
const windowWidth = window.innerWidth
const windowWidth = ref(0)
onMounted(() => {
windowWidth.value = window.innerWidth
})
<ClientOnly>
<BrowserOnlyComponent />
</ClientOnly>
<span>{{ new Date().toLocaleString() }}</span>
<ClientOnly>
<span>{{ formattedDate }}</span>
<template #fallback>Loading...</template>
</ClientOnly>
Debugging Steps:
- Check browser console for specific mismatch details
- Look for
window, document, localStorage usage outside onMounted or process.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:
function fetchData() {
const { data } = useFetch('/api/data')
}
const { data, error, pending, refresh } = useFetch('/api/data')
async function fetchData() {
const data = await $fetch('/api/data')
}
Key/Caching Issues:
const { data: user1 } = useFetch('/api/user', { key: 'user' })
const { data: user2 } = useFetch('/api/user', { key: 'user' })
const { data: user1 } = useFetch('/api/user/1', { key: 'user-1' })
const { data: user2 } = useFetch('/api/user/2', { key: 'user-2' })
const { data, refresh } = useFetch('/api/data')
await refresh()
Watch for Reactive Parameters:
const userId = '123'
const { data } = useFetch(`/api/user/${userId}`)
const userId = ref('123')
const { data } = useFetch(() => `/api/user/${userId.value}`)
const { data } = useFetch('/api/user', {
query: { id: userId },
watch: [userId]
})
3. Nitro Server Errors
500 Internal Server Errors:
export default defineEventHandler(async (event) => {
const data = await fetchExternalAPI()
return data
})
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:
export default defineEventHandler(async (event) => {
const body = event.body
const body = await readBody(event)
const query = getQuery(event)
const { id } = event.context.params
})
4. Plugin Initialization Issues
export default defineNuxtPlugin(() => {
const api = new ExternalAPI()
})
export default defineNuxtPlugin({
name: 'my-plugin',
enforce: 'pre',
async setup(nuxtApp) {
try {
const api = new ExternalAPI()
return {
provide: {
api
}
}
} catch (error) {
console.error('Plugin initialization failed:', error)
}
}
})
export default defineNuxtPlugin({
name: 'client-only-plugin',
setup() {
}
})
5. Module Conflicts
Diagnosing Module Issues:
export default defineNuxtConfig({
modules: [
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
],
debug: true,
})
Common Module Conflicts:
rm -rf node_modules/.cache
rm -rf .nuxt
rm -rf node_modules
npm install
Debugging Tools
1. Nuxt DevTools (Recommended)
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
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)
nuxi dev --inspect
nuxi dev --inspect=0.0.0.0
5. Console Logging (Server vs Client)
console.log('Universal log')
if (process.server) {
console.log('Server-side only')
}
if (process.client) {
console.log('Client-side only')
}
const nuxtApp = useNuxtApp()
if (nuxtApp.ssrContext) {
console.log('Server-side render')
}
6. Vue DevTools
npx @vue/devtools
The Four Phases of Nuxt Debugging
Phase 1: Identify the Context
Determine where the error occurs:
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
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)
})
const nuxtApp = useNuxtApp()
console.log('Payload:', nuxtApp.payload)
Phase 4: Verify Build and Config
nuxi typecheck
nuxi analyze
rm -rf .nuxt .output node_modules/.cache
nuxi build
Quick Reference Commands
Development
nuxi dev
nuxi dev --inspect
nuxi dev --port 3001
nuxi dev --https
Building and Analysis
nuxi build
nuxi generate
nuxi preview
nuxi analyze
nuxi typecheck
nuxi prepare
Maintenance
nuxi cleanup
nuxi upgrade
nuxi module add @nuxtjs/tailwindcss
nuxi add component MyComponent
nuxi add page about
nuxi add composable useMyComposable
nuxi add api hello
Error Handling Patterns
Global Error Handler
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 createError({
statusCode: 404,
statusMessage: 'Page not found',
fatal: true
})
showError({
statusCode: 500,
statusMessage: 'Something went wrong'
})
clearError({ redirect: '/' })
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
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
const { data } = await useFetch('/api/data')
const { data, pending } = useLazyFetch('/api/data')
watch(data, (newData) => {
if (newData) {
console.log('Data loaded:', newData)
}
})
Server-Only Code Leaking to Client
export default defineNuxtConfig({
runtimeConfig: {
apiSecret: '',
public: {
apiBase: ''
}
}
})
const config = useRuntimeConfig()
Performance Debugging
Identify Slow Components
export default defineNuxtConfig({
experimental: {
componentIslands: true
}
})
<!-- Use islands for heavy server components -->
<NuxtIsland name="HeavyChart" :props="{ data: chartData }" />
Lazy Loading
const HeavyComponent = defineAsyncComponent(() =>
import('~/components/HeavyComponent.vue')
)
<template>
<LazyHeavyComponent v-if="showHeavy" />
</template>
Bundle Analysis
nuxi analyze
cat .output/public/_nuxt/builds/meta/*.json | jq
Common Gotchas
1. Composables Must Be Called in Setup
function handleClick() {
const route = useRoute()
}
const route = useRoute()
function handleClick() {
console.log(route.path)
}
2. Reactive Data in useFetch
const id = '123'
useFetch(`/api/items/${id}`)
const id = ref('123')
useFetch(() => `/api/items/${id.value}`)
3. Navigate vs Router
await navigateTo('/dashboard')
await navigateTo({ path: '/user', query: { id: 1 } })
export default defineEventHandler((event) => {
return sendRedirect(event, '/login', 302)
})
4. Middleware Execution Order
definePageMeta({
middleware: ['auth', 'premium']
})
5. State Pollution in SSR
const globalState = reactive({})
const state = useState('key', () => ({}))
const store = useMyStore()
Resources