
Blazor WASM Exception Handling and Error Boundary
Author - Abdul Rahman (Bhai)
Blazor
34 Articles
In this article, lets learn about exception handling and error boundary in blazor WASM applications.
Note: If you have not done so already, I recommend you read the article on Blazor WASM Javascript Interop and Isolation.
Table of Contents
Introduction
Exceptions are common in all apps. We can handle known exceptions which we expect using try catch blocks. But this cannot be the case always. Some times we might end up in unforeseen run time unhandled exceptions. There are different ways to handle exceptions. Luckily with blazor we have exception handling built in in two different ways.
<button type="button" @onclick="ConnectToAgent">Click to initiate chat with agent</button>
@code {
protected static void ConnectToAgent()
{
throw new Exception();
}
}
Global Exception Handling
Blazor has global error handling element <div id="blazor-error-ui"> in index.html. This will act as an global error handler which will catch any unhanled exception across blazor app and display nice error UI codesent inside <div id="blazor-error-ui"> at the bottom of the screen by default and logs the error stack trace to browser console. This will also have reload option to reload the app to restore the app to correct working state.
<div id="blazor-error-ui" class="[ bg-red-500 ] [ text-white ] [ fixed bottom-0 ] [ w-full ] [ p-2 ]" style="display: none;">
An unhandled error has occurred. Please click on <a href="javascript: void(0)" onclick="location.reload()">Reload</a> to proceed.
<button>X</button>
</div>
Error Boundary
Though <div id="blazor-error-ui"> handles all unhandled exceptions it will be nice to restrict the error handling boundary and have better control of the error message and error UI. That said .Net 6 introduced <ErrorBoundary> to serve this purpose. We can wrap any component inside <ErrorBoundary> and this will take care of showing error UI when an unhandled exception occurs inside the component.
<ErrorBoundary>
<Chat></Chat>
</ErrorBoundary>
We can control the error message with our own custom error message and error UI using <ErrorContent>. From now incase of any unhandled exception, our custom error UI will be displayed.
<ErrorBoundary>
<ChildContent>
<Chat></Chat>
</ChildContent>
<ErrorContent>
<div class="[ bg-red-500 ]">
<p class="[ text-white ]">An error occured while establishing a connection. Please try again later.</p>
</div>
</ErrorContent>
</ErrorBoundary>
So far so good. But doesn't this leave a bad user experience? How will the user retry the action? One way is to reload the page. But there is an another easy option called Recover in ErrorBoundary. All we need to do is to add a reference to Error Boundary and call errorBoundary?.Recover() method to retry the action.
<ErrorBoundary @ref="@errorBoundary">
<ChildContent>
<Chat></Chat>
</ChildContent>
<ErrorContent>
<p>An error occured while establishing a connection. Please try again later.</p>
</ErrorContent>
</ErrorBoundary>
<button type="button" @onclick="Recover">Click to recover</button>
@code {
protected ErrorBoundary errorBoundary = default!;
protected void Recover()
{
errorBoundary?.Recover();
}
}
Alternative Global Exception Handling
An alternative to using Error boundaries (ErrorBoundary) is to pass a custom Error component as a CascadingValueto child components. An advantage of using a component over using an injected service or a custom logger implementation is that a cascaded component can render content and apply CSS styles when an error occurs.
@using Microsoft.Extensions.Logging
@inject ILogger<Error> Logger
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
@code {
[Parameter] public RenderFragment? ChildContent { get; set; }
public void ProcessError(Exception ex)
{
//Post Exception Details to API. For simplicity I'm logging to browser console.
Logger.LogError("Error:ProcessError - Type: {Type} Message: {Message}", ex.GetType(), ex.Message);
}
}
Now in the App component, wrap the Router component with the Error component. This permits the Error component to cascade down to any component of the app where the Error component is received as a CascadingParameter. We can then call error processing method ProcessError from Error component to handle error. To learn more about this, head over to Blazor Wasm Error Logging.
<Error>
<Router ...>
...
</Router>
</Error>
Summary
I'm happy that you have reached to the end of this article. Here we leant about how global exception handler works in blazor wasm apps and we also learnt how to control exceptions with ErrorBoundary component to give better experience to users with recovery options.