Custom Error Pages in Nuxt 3

This article dives into how to create a custom error page for Nuxt 3 applications to replace Nuxt 3's built in error page.

Michael Thiessen
Nuxt 3

Mastering Nuxt 3 course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view the Nuxt 3 course

Nuxt 3 has a ton of built-in features that just work without requiring anything of you — including an error page.

But while it’s great to have a default page, we usually want to add our own custom error pages.

In this article, we’ll dive into how we can write our own error page and customize it based on the error code. We’ll also see how we can recover from errors and get back to a good state in our app.

Let’s go!

Creating the Error Page

To create an error page, we need to add an error.vue file to the root of our application, alongside our app.vue and nuxt.config.ts files.

This is important, because this server page is not a page. Not all Nuxt 3 apps have pages or use Vue Router, so we can’t rely on Vue Router to display the error page for us.

A super basic error page might look something like this:

<template>
  <NuxtLayout>
    <div>
      <h1>Dang</h1>
      <p>It looks like something broke.</p>
      <p>Sorry about that.</p>
    </div>
  </NuxtLayout>
</template>

Not super helpful, but we can make it better by using the useError composable to grab more details about the global error:

<script setup>
const error = useError();
</script>

Now, we can add a more descriptive message to our error page:

<template>
  <NuxtLayout>
    <div class="prose">
      <h1>Dang</h1>
            <p>
              <strong>{{ error.message }}</strong>
            </p>
      <p>It looks like something broke.</p>
      <p>Sorry about that.</p>
    </div>
  </NuxtLayout>
</template>

We’re not done yet, but I need to explain more about this global error thing.

Global Errors vs. Client-Side Errors

In Nuxt we have two types of errors:

  • Global errors: these errors can be thought of as “server-side” errors, but they’re still accessible from the client
  • Client-side errors: these errors only exist on the client, so they don’t affect the rest of your app and won’t show up in logs unless you use a logging service like LogRocket (there are many of these services out there).

It’s important to understand this distinction because these errors happen under different circumstances and need to be handled differently.

Global errors can happen any time the server is executing code. Mainly, this is during an API call, during a server-side render, or any of the code that glues these two together.

Client-side errors mainly happen while interacting within an app, but they can also happen on route changes because of how Nuxt’s Universal Rendering works.

It’s important to note that the NuxtErrorBoundary component only deals with client-side errors, and does nothing to handle or clear these global errors.

We have a different method for that.

Clearing Errors

When an error occurs, we need to recover from that error somehow.

We won’t get into that in this article, since that’s a whole lengthy topic on its own. But the most basic thing we can do is reset the error and then navigate somewhere else using the clearError method:

<script setup>
const error = useError();

const handleError = () => {
  clearError({
    redirect: '/dashboard',
  });
};
</script>

Then we just need to hook up this handler to our template:

<template>
  <NuxtLayout>
    <div class="prose">
      <h1>Dang</h1>
            <p>
              <strong>{{ error.message }}</strong>
            </p>
      <p>It looks like something broke.</p>
      <p>Sorry about that.</p>
      <p>
            Go back to your 
            <a @click="handleError">
              dashboard.
            </a>
          </p>
    </div>
  </NuxtLayout>
</template>

When the user clicks on the link, the global error will be cleared in the Nuxt app and they’ll be redirected to the /dashboard url.

Handling 404s with a Custom Page

So far, this page can only show whatever message is supplied through the error object.

But what if we want to show a completely different page if there’s a 404 error?

Instead of creating a totally separate page, we just need to check the statusCode on the error object. From there, we can show different messages and customize our error page however we need.

The error object we get back from useError has the following structure:

error = {
  statusCode: Number;
  statusMessage: String;
  message: String;
}

We’ll update our error page to include a check for this statusCode:

<template>
  <NuxtLayout>
    <div class="prose">
        <template v-if="error.statusCode === 404">
          <h1>404!</h1>
          <p>Sorry, that page doesn't exist.</p>
        </template>
            <template v-else>
          <h1>Dang</h1>
                <p>
                  <strong>{{ error.message }}</strong>
                </p>
          <p>It looks like something broke.</p>
          <p>Sorry about that.</p>
      </template>
      <p>
            Go back to your 
            <a @click="handleError">
              dashboard.
            </a>
          </p>
    </div>
  </NuxtLayout>
</template>

Using this strategy we’re able to render what we need based on the type of error and the message that the error contains.

Conclusion

Nuxt makes error handling a whole lot simpler for us.

We can create a custom error page, handling different errors however we need, and then gracefully recover and help the user get back to a functioning app.

You can learn more about error handling in Nuxt by checking out the docs.

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on