One File Catches Everything
error.vue — Polishing 404s and 500s
A single error.vue at the project root replaces both the 404 and 500 pages, with full access to your branding, layout, and components.
What you'll learn
- Differentiate 404 vs 500 by error.statusCode
- Return users home with clearError
- Style errors with your full app branding
Nuxt has its own ugly default error page. Replace it by creating error.vue at
the project root — same level as app.vue. It receives the error as a prop and
catches both 404s and uncaught 500s.
The File
<script setup lang="ts">
const props = defineProps<{
error: { statusCode: number; message: string }
}>()
const handleHome = () => clearError({ redirect: '/' })
</script>
<template>
<div class="error">
<h1 v-if="error.statusCode === 404">Page not found</h1>
<h1 v-else>Something went wrong</h1>
<p>{{ error.message }}</p>
<button @click="handleHome">Back home</button>
</div>
</template> Differentiating Status Codes
error.statusCode is the HTTP code — 404 for missing routes, 500 for server
errors, anything you throw createError({ statusCode: 403 }) from middleware.
<template>
<div v-if="error.statusCode === 404">
<h1>404</h1>
<p>We couldn't find that page.</p>
</div>
<div v-else-if="error.statusCode === 403">
<h1>Forbidden</h1>
<p>You don't have access.</p>
</div>
<div v-else>
<h1>Server error</h1>
<p>Try again in a moment.</p>
</div>
</template> clearError
Calling clearError() removes the error from Nuxt’s state — the user sees the
normal app again. Pass { redirect: '/' } to also navigate.
// Clear and stay on the current URL
clearError()
// Clear and go home
clearError({ redirect: '/' }) Throwing Errors From Pages
Anywhere in your app, throw createError({ statusCode: 404 }) jumps to
error.vue with the right status. Useful in data calls that 404 on the server.
const { data: post } = await useFetch(`/api/posts/${slug}`)
if (!post.value) throw createError({ statusCode: 404, statusMessage: 'Post not found' }) Layout Note
error.vue does not use your layouts by default — it’s a stand-alone page. If
you want the normal layout, wrap the template in <NuxtLayout>:
<template>
<NuxtLayout name="default">
<h1>Oops</h1>
</NuxtLayout>
</template>