If you’re building a Progressive Web Application (PWA), chances are you’ll want to show the Online/Offline status of the browser to help keep the user informed. Fortunately, there is an incredibly simple Browser API for accessing this data! This article discusses how we’ll integrate the javascript API into a simple Blazor component. This article assumes basic knowledge of setting up a Blazor project and/or component library.

The first step is to build out the javascript function that allows us to notify the Blazor/.NET side that the Online status has changed. Head over to the js interop file in your project, or make a new one. This function will take a special “.net reference” that allows us to invoke .NET methods from javascript (usually we go the other way!). We simply listen to the online and offline methods and notify the reference through a known interface (method).

window.addOnlineStatusListener = function (dotNetRef) {
    function updateDotNet() {
        dotNetRef.invokeMethodAsync('UpdateStatus', navigator.onLine);
    };

    window.addEventListener('online', updateDotNet);
    window.addEventListener('offline', updateDotNet);

    updateDotNet();
}

Now, on the Blazor side, we need an object that can both call this method and pass in a reference [to itself] to be notified of changes through the invokeMethodAsync callback. That means building a class that utilizes the IJSRuntime.

public class OnlineStatusInterop : IAsyncDisposable
{
    private readonly IJSRuntime _jsRuntime;
    private DotNetObjectReference<OnlineStatusInterop> _reference;
    private bool _isInitialized;

    public event EventHandler<bool>? OnlineStatusChanged;

    public OnlineStatusInterop(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
        _reference = DotNetObjectReference.Create(this);
    }

    public async Task Initialize()
    {
        if (_isInitialized) { return; }

        await _jsRuntime.InvokeVoidAsync("addOnlineStatusListener", _reference);

        _isInitialized = true;
    }

    [JSInvokable]
    public void UpdateStatus(bool isOnline)
    {
        OnlineStatusChanged?.Invoke(this, isOnline);
    }

    public ValueTask DisposeAsync()
    {
        _reference?.Dispose();
        return ValueTask.CompletedTask;
    }
}

If you wish to have this dependency injected, you can then add this to the client side’s service collection.

services.AddScoped<OnlineStatusInterop>();

And finally, we need to build the component that actually renders out some interesting HTML to look at! In many of my projects, I employ the use of Heroicons and Tailwind CSS – that will be no different here. The component simply uses a dependency injected instance of the OnlineStatusInterop class and uses the OnlineStatusChanged event to update a local boolean, notify any callers and update the UI. We also add in the simple capability to override the default renderings for both Online and Offline so that callers may customize them.

@inject OnlineStatusInterop OnlineStatusInterop
<div class="online-status-indicator">
    @if(IsOnline)
    {
        if(Online != null)
        {
            @Online
        }
        else
        {
            <svg xmlns="http://www.w3.org/2000/svg" class="inline-block text-green-500 h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
            </svg>
        }
    }
    else
    {
        if(Offline != null)
        {
            @Offline
        }
        else
        {
            <svg xmlns="http://www.w3.org/2000/svg" class="inline-block text-red-500 h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
            </svg>
        }
    }
</div>

@code {
    [Parameter]
    public bool IsOnline { get; set; }

    public EventCallback<bool>? IsOnlineChanged { get; set; }

    [Parameter]
    public RenderFragment? Online { get; set; }

    [Parameter]
    public RenderFragment? Offline { get; set; }

    protected override async Task OnInitializedAsync()
    {
        OnlineStatusInterop.OnlineStatusChanged += (s, v) =>
        {
            IsOnline = v;
            IsOnlineChanged?.InvokeAsync(v);
            StateHasChanged();
        };

        await OnlineStatusInterop.Initialize();
        
    }
}

And that’s it! When the browser changes to Offline mode, the Offline indicator will be shown. You can find the full source code as part of the Tazor library.