error.vue at the Project Root
Custom Error Pages
A root-level error.vue handles 404 and 500 responses. Use clearError to recover and return the user to safe ground.
What you'll learn
- Create error.vue at the project root
- Read the error prop and branch by status code
- Call clearError to recover from a runtime error
When a route does not match, validation fails, or a runtime error escapes a page, Nuxt looks for a project-level error.vue. It is your single fallback UI for the whole app.
Anatomy
error.vue lives next to app.vue (not inside pages/). It receives an error prop with at least statusCode, statusMessage, and message.
<!-- error.vue -->
<script setup lang="ts">
const props = defineProps<{
error: {
statusCode: number
statusMessage?: string
message: string
}
}>()
function goHome() {
clearError({ redirect: '/' })
}
</script>
<template>
<section class="error">
<h1>{{ props.error.statusCode }}</h1>
<p>{{ props.error.statusMessage ?? props.error.message }}</p>
<button @click="goHome">Go home</button>
</section>
</template> clearError resets the error state. With a redirect option it also navigates somewhere safe.
Branching by Status
Treat 404 and 500 differently — a missing post needs a friendlier nudge than a backend crash.
<template>
<template v-if="error.statusCode === 404">
<h1>Page not found</h1>
<p>The link may be old or mistyped.</p>
</template>
<template v-else>
<h1>Something broke</h1>
<p>Our team has been notified.</p>
</template>
</template> Throwing Errors From Pages
Inside a page or composable, use createError to surface a status that triggers error.vue:
const { data: post } = await useFetch(`/api/posts/${slug}`)
if (!post.value) {
throw createError({ statusCode: 404, statusMessage: 'Post not found' })
} On the server, this also sets the HTTP status code — search engines see a proper 404. On the client, Nuxt swaps the page for error.vue instantly.
This wraps up routing. Next we move into data fetching — bringing your app to life with real content.
Data Fetching →