Nuxt 3 Client-Side Error Handling

It may not be as fun as shipping the next feature, but making sure our apps are rock-solid and can recover from errors is crucial. Without good error-handling, our UX suffers and our applications can be hard to use.

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

No production app is complete without proper error-handling.

It may not be as fun as shipping the next feature, but making sure our apps are rock-solid and can recover from errors is crucial. Without good error-handling, our UX suffers and our applications can be hard to use.

There are 3 main types of error handling we need to add to our Nuxt apps:

  • Client-side
  • Server-side (during rendering)
  • API errors

We’ll be focusing on client-side error-handling in this article.

Nuxt 3 comes with the NuxtErrorBoundary component which makes handling client-side errors a breeze.

Using the NuxtErrorBoundary Component

When an error happens in your Vue app, it bubbles up until something catches it. If nothing catches it, your whole app crashes.

The NuxtErrorBoundary component will catch any of the errors that happen in its default slot. This lets us recover from them and handle them properly.

<NuxtErrorBoundary>
  <!-- Put components in here -->
</NuxtErrorBoundary>

Of course, when an error happens we want to let the user know.

Using the error named slot and the error object that’s passed into it, we can put in whatever error message we want:

<NuxtErrorBoundary>
  <VideoPlayer />
  <template #error="{ error }">
    <div>
      <p>Oops, it looks like the video player broke :/</p>
      <p>{{ error.message }}</p>
    </div>
  </template>
</NuxtErrorBoundary>

Recovering from Client-Side Errors in Nuxt 3

Once we’ve shown the error message, it’s now time to do something about it.

In order to clear this error, we need to set the value of this error ref to null.

<NuxtErrorBoundary>
  <VideoPlayer />
  <template #error="{ error }">
    <div>
      <p>Oops, it looks like the video player broke :/</p>
      <p>{{ error.message }}</p>
      <p><button @click="recoverFromError(error)">Try again</button></p>
    </div>
  </template>
</NuxtErrorBoundary>
const recoverFromError = (error) => {
  // Try to resolve the error here
  error.value = null;
}

Note: Nuxt has composables and utilities like useError and clearError, but these operate on the server error. The error object that NuxtErrorBoundary uses is specific to the instance of the component and only exists on the client-side.

Inside of recoverFromError you should try to resolve any potential errors. This will be based on what’s being rendered in the default slot.

In many cases simply re-rendering and trying again will be enough. But you may need to retry an API call or do something else in order to deal with the situation. This is highly dependent on the situation.

Sometimes when you’re handling an error, resolving it like this doesn’t quite quite cut it.

In that case, you’ll need to do something far more dramatic.

When we set error to null, the NuxtErrorBoundary component will re-render the default slot. If what was causing the error isn’t resolved, we get stuck in a loop — reset, error, reset, error…

The solution is to navigate somewhere safe.

We can do this using the navigateTo utility. It works on both client-side and server-side code, so we can use it all over our Nuxt apps.

<NuxtErrorBoundary>
  <VideoPlayer />
  <template #error="{ error }">
    <div>
      <p>Oops, it looks like the video player broke :/</p>
      <p>{{ error.message }}</p>
      <p><button @click="recoverFromError(error)">Go back home</button></p>
    </div>
  </template>
</NuxtErrorBoundary>
const recoverFromError = async (error) => {
  await navigateTo('/');
  error.value = null;
}

Here we’re navigating to the home page first, then clearing the error. If we try to clear the error before navigating, we’ll get stuck in that loop.

Because Nuxt 3 operates as a single page app after the initial page load, if we don’t clear this error when navigating, this error message may linger even though we’re on a different route!

Please note, simply navigating somewhere different won’t solve all your errors. It could be that the app will still crash. But at least we’re doing our best to give our users a chance to recover from the error.

Where to Use NuxtErrorBoundary

The benefit of an error boundary is that we can contain errors within our application, and then handle them in specific ways instead of just throwing up a generic error page.

My suggestion would be to place NuxtErrorBoundary components around distinct chunks of functionality — from the user’s perspective — where you can handle a group of potential errors together.

For example, wrapping a NuxtErrorBoundary around each widget in an admin dashboard.

This also means that child routes should probably get their own boundary, since each child route represents distinct functionality within your app. And each child route will have different errors and different error messages from the rest of the app.

For example, in the Mastering Nuxt 3 course we have our lessons rendered as a child route. We put a boundary around it so that if something breaks when rendering the lesson, we’re able to give a useful error message.

<NuxtErrorBoundary>
  <NuxtPage />

  <template #error="{ error }">
    <div>
      <p>
        Oh no, something broke when loading the lesson!
        <code>{{ error }}</code>
      </p>
      <p>
        <button
          class="hover:cursor-pointer"
          @click="clearError(error)"
        >
          Go to the first lesson
        </button>
      </p>
    </div>
  </template>
</NuxtErrorBoundary>

We can also provide a better recovery method, since we know that what broke was loading a lesson. If we have a generic error page, we have no clue what could have caused the error.

const clearError = async (err) => {
  // Go to the first lesson
  await navigateTo(
    '/course/chapter/1-chapter-1/lesson/1-introduction-to-typescript-with-vue-js-3'
  );
  err.value = null;
};

Conclusion

By using the NuxtErrorBoundary component, we’re able to isolate errors from the rest of our app.

This makes it easier to handle any errors intelligently, and provide our users with a much better user experience when errors do happen.

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