Nuxt error.vue: Designing Custom Error Pages
AI generated
{ }
Nuxt 3 · Error Handling · UX
Nuxt error.vue: Designing Custom Error Pages
How Nuxt automatically forwards runtime errors, and how clearError() enables recovery

Nuxt automatically catches unhandled runtime errors and renders the error.vue file in the project root in their place. clearError() lets you deliberately leave the error state again, while the display can be tailored individually to 404 or 500 situations depending on the status code.

14 min read error.vue · Nuxt 3 Error Handling

1. How Nuxt Automatically Forwards Runtime Errors

When a page, a middleware, or a server handler in Nuxt throws an unhandled error, Nuxt catches it automatically and switches into a global error state. Instead of the normal page, the error.vue file in the project root then gets rendered, serving as a kind of global fallback for every error situation across the entire project.

This mechanism applies both to errors that occur during server-side rendering, such as a failed database call inside useAsyncData, and to errors that only surface later in the browser, such as a failed API call after a user interaction. In both cases, error.vue gets access to the details of the error through a dedicated prop.

2. The Structure of error.vue

error.vue lives directly in the project root, at the same level as app.vue, rather than inside the pages directory, since it's a special file reserved by Nuxt. The component receives the error that occurred as a prop named error, which includes fields such as statusCode, statusMessage, and message.

Since error.vue renders outside the normal layout system, the component has to bring its own complete HTML skeleton, including its own header and footer if those should also be visible on the error page. An existing default.vue layout does not get applied to the error page automatically.

3. clearError() for Recovery Actions

The global error state in Nuxt persists until it's explicitly left again. For that, Nuxt provides the clearError() function, which resets the error state and optionally redirects to another route. A typical use case is a button on the error page labeled Back to Home that calls clearError() with a target route when clicked.

Without calling clearError(), the application would remain stuck in the error state indefinitely, even if the user tries to navigate to a working page through the browser. This explicit reset is therefore a necessary part of any well-designed error page and should never be forgotten.


<!-- error.vue -->
<template>
  <div class="error-page">
    <h1>{{ error.statusCode }}</h1>
    <p v-if="error.statusCode === 404">
      Sorry, this page could not be found.
    </p>
    <p v-else>
      Something went wrong on our end.
    </p>
    <button @click="handleRetry">Back to Home</button>
  </div>
</template>

<script setup lang="ts">
import type { NuxtError } from '#app';

const props = defineProps<{ error: NuxtError }>();

function handleRetry() {
  clearError({ redirect: '/' });
}
</script>

4. Different Display Depending on the Status Code

Since error.statusCode is available as a reactive value inside error.vue, the display can be tailored to the specific status code without extra effort. A 404 status generally warrants a friendly, non-alarming text with suggestions for further navigation, while a 500 status deserves more of an apology, possibly along with a note that the issue has been reported automatically to the development team.

In practice, a small lookup table or a switch statement inside error.vue is worthwhile, providing a fitting heading, text, and recommended actions for the most common status codes like 404, 403, and 500, rather than showing the same generic text for every error.

5. Difference Between Server-Side and Client-Side Errors

A server-side error, such as a failed database call during the initial render, causes the browser to receive the rendered error.vue page directly along with the corresponding HTTP status code. In this case, search engines and other crawlers correctly see a 404 or 500 status code in the HTTP response, which matters for SEO purposes.

A client-side error that only occurs after hydration in the browser, for instance triggered by a failed user action, does not change the page's original HTTP status code, since that response has long since completed. Instead, the application switches into the error state on the client and displays error.vue as an overlay on top of the existing page, without a new server call carrying a new status code.

6. Throwing Custom Errors Deliberately

The createError() function lets you deliberately create custom errors with a chosen status code and message inside pages, middleware, or server handlers. This is especially useful when a resource technically loaded successfully but doesn't exist in a meaningful sense, such as a product with an invalid ID that should be treated as a 404 on the server.

When the optional fatal: true parameter is passed to createError(), Nuxt immediately forces a switch into the global error state and renders error.vue, rather than treating the error as a local state that a component could handle on its own. Without this flag, an error can, in certain contexts, also be caught locally without replacing the entire page.

7. Layout and Styling of the Error Page

Since error.vue doesn't automatically receive a layout from the layouts directory, it's advisable to extract shared building blocks like the header and footer into their own reusable components and include them both in the normal layout and directly inside error.vue. That keeps the error page's appearance consistent with the rest of the application without duplicating markup.

For Tailwind-based projects, styling inside error.vue works exactly like in any other component, as long as the global styles are wired up correctly. A common mistake is overlooking error.vue while testing and only discovering in production that it visually doesn't match the rest of the application.

8. showError() for Programmatically Triggering the Error State

Besides createError(), which is typically used together with a throw, Nuxt offers showError() as an alternative for triggering the global error state directly, without an exception. This is useful, for example, in a global error handler that reacts to an unhandled promise rejection event and only then decides whether the entire application should switch into the error state.

While createError() primarily produces an error object that then gets thrown, showError() directly performs the switch into the global state and renders error.vue. In practice, both functions are often used together: createError() to produce a consistent error object, and showError() to deliberately activate the error state outside the regular rendering cycle.

9. Conclusion: A Well-Designed Error Page Belongs to the Application

A well-designed error.vue is more than a technical necessity, it's an integral part of the user experience, especially in moments when something has gone wrong. Instead of the generic default Nuxt error page, every production project should provide its own, on-brand error page with clear recommended actions and working recovery paths.

Anyone who consistently uses clearError(), tailors the display to the status code, and understands the difference between server-side and client-side errors builds an error page that doesn't leave users stranded, but instead shows them a clear way back to a working page.

Aspect Server-Side Error Client-Side Error
Timing During SSR / initial request After hydration, in the browser
HTTP status code Set correctly in the response Original response already completed
SEO relevance High, crawlers see the real status code Low, no new HTTP response
Typical cause Failed useAsyncData fetch Failed user action, API call
Reset clearError() with optional redirect clearError() with optional redirect

Mironsoft

Vue architecture, Composition API, and Nuxt performance

Vue applications that don't get more complicated with every feature?

We review existing Vue and Nuxt projects for unstructured composables, unnecessary reactivity, and bloated bundles, then build an architecture that absorbs new features without making the codebase harder to follow.

Architecture Review

Checking composables, state management, and component structure for maintainability.

Performance Audit

Systematically optimizing reactivity overhead, bundle size, and Nuxt rendering strategy.

Nuxt Integration

Building robust, type-safe SSR/SSG setup and API integration.

10. Summary

Nuxt error.vue: The Essentials at a Glance

File

error.vue in the project root, outside the pages directory

Recovery

clearError() deliberately resets the global error state

Status code access

error.statusCode as a reactive prop for tailored display

Custom errors

createError() with fatal: true forces the global error state

11. FAQ: Nuxt error.vue: The Essentials at a Glance

1Where exactly does the error.vue file need to live?
Directly in the project root, at the same level as app.vue, not inside the pages directory. Nuxt automatically recognizes this filename as the global error page for the entire project.
2Does error.vue automatically get the application's normal layout?
No, error.vue renders outside the regular layout system and therefore has to bring its own complete HTML skeleton, including its own header and footer if desired.
3What happens if I never call clearError()?
The application stays stuck in the global error state indefinitely, even if the user tries navigating to another page through links or the browser. clearError() is therefore required for any recovery action.
4How do I distinguish between 404 and 500 inside error.vue?
Through the reactive error.statusCode prop, which holds the corresponding HTTP status code. A simple condition or a switch statement can derive a fitting heading and recommended action from it.
5Can I throw my own errors with a specific status code?
Yes, via createError({ statusCode: 404, statusMessage: '...' }) you can deliberately create custom errors with the desired status code inside pages, middleware, or server handlers.
6What does the fatal flag on createError() do?
With fatal: true, the error immediately forces a switch into the global error state and renders error.vue. Without this flag, the error can, in some contexts, also be handled locally without triggering the global error state.
7Does a client-side error change the page's HTTP status code?
No, the original HTTP response has already completed by that point. The application merely switches into the error state locally in the browser and displays error.vue as an overlay.
8Is error.vue relevant for search engines?
Yes, for server-side errors, Nuxt sends the correct HTTP status code such as 404 or 500 along with the rendered error.vue page, which matters for crawlers and therefore for SEO.
9Can I use useFetch or useAsyncData inside error.vue?
In principle yes, but care should be taken that a fresh error inside error.vue itself doesn't lead to a situation that can no longer be handled meaningfully, since the higher-level error handling is already active.
10Does every Nuxt application need its own error.vue?
Not strictly, technically, since Nuxt otherwise shows a generic default error page. For a professional, on-brand appearance, though, a custom error.vue is advisable in practically every production project.