33
Handling errors with Error Boundaries in Svelte
In this post you'll learn how to create an Error Boundary in Svelte, similar to the functionality introduced in React 16. This functionality allows individual components to fail rendering while still keeping the rest of the application working.
I was surprised to see that in Svelte there was no straightforward way to define error boundaries or use any similar mechanism to avoid application crashes. You can see some existing proposals in the form of GitHub issues here, here and here.
Thankfully there is a community package available that implements error boundaries! 🤩
First, let's install the package:
npm i @crownframework/svelte-error-boundary
Now we can use it in our components. For example, this component immediately throws an error due to missing data, but wrapping the whole component with a Boundary
avoids crashing the Svelte application. (See REPL here.)
<!-- User.svelte -->
<script>
import { Boundary } from '@crownframework/svelte-error-boundary';
const user = null;
</script>
<!-- The code inside this boundary will throw immediately -->
<Boundary onError={console.error}>
{user.name}
</Boundary>
The result:
ℹ️ By default, the error component won't show the full trace in production.
ℹ️ The onError
attribute can be used to perform additional error logging, such as to the console or to an external service like Sentry.
Customizing the error boundary
I find that many components need their own bespoke error boundary. For an upcoming chat app I wanted the boundary for failed messages to look like an actual message, for example:
Let's see how we can accomplish this! First we need to define our own Error Boundary component. It turns out that these error boundary components are just normal Svelte components with a <slot>
for where the non-error content should be placed. Let's make an error component based on the default component that the package provides. We will also style it ourselves:
<!-- UserError.svelte -->
<script>
export let error = null;
export let onError = null;
$: if ($error && onError) onError($error);
</script>
{#if $error}
<div class="error">
<em>User could not be shown</em>
</div>
{:else}
<slot />
{/if}
<style>
.error {
background-color: #666;
color: white;
padding: 10px;
}
</style>
Now we can import this component into our earlier User.svelte
component and convert it to an error boundary using the createBoundary
function:
<!-- User.svelte -->
<script>
import UserError from './UserError.svelte';
import { createBoundary } from '@crownframework/svelte-error-boundary';
const user = null;
const Boundary = createBoundary(UserError);
</script>
<!-- The code inside this boundary will throw immediately -->
<Boundary onError={console.error}>
{user.name}
</Boundary>
You should now see our custom error boundary component! (See REPL here.)
Finishing thoughts and caveats
Keep in mind that if you are just waiting for data to arrive from an external API that uses a Promise-based mechanism, you should use await blocks that already have error handling through {:catch error}
.
I've also noticed that this package has an issue with Vite and HMR - the error is shown on initial page load but after HMR the error component disappears completely. This won't affect production though!
33