Browser Web Storage #

Havit.Blazor.Storage provides strongly-typed access to the browser Web Storage areas – window.localStorage and window.sessionStorage – from your Blazor application. It exposes two services, ILocalStorageService and ISessionStorageService, both implementing the same IBrowserStorage interface. You can store and retrieve plain string values or any object (serialized to/from JSON).

Installation #

1. NuGet package #

Add the Havit.Blazor.Storage NuGet package to your Blazor project. In a Blazor Web App with a separate .Client project, add it to the project where you intend to consume the storage (typically the .Client project, so that the synchronous API is available in the WebAssembly render mode).

dotnet add package Havit.Blazor.Storage

2. Register services #

Register the services in your Program.cs:

using Havit.Blazor.Storage;

builder.Services.AddHavitBlazorStorage();

This registers both ILocalStorageService and ISessionStorageService with a Scoped lifetime. No <script> reference is required – the small supporting JavaScript module is loaded automatically by the Blazor JS initializer.

Basic usage #

Inject ILocalStorageService and/or ISessionStorageService into your component. Both share the same API (IBrowserStorage), so the only difference is which browser storage area they target.

@inject ILocalStorageService LocalStorage

<button @onclick="StoreAsync">Store</button>
<button @onclick="LoadAsync">Load</button>
<p>@_value</p>

@code {
    private string _value;

    private async Task StoreAsync()
    {
        await LocalStorage.SetStringValueAsync("greeting", "Hello world!");
    }

    private async Task LoadAsync()
    {
        var (success, value) = await LocalStorage.TryGetStringValueAsync("greeting");
        _value = success ? value : "<not stored yet>";
    }
}

Synchronous vs. asynchronous API #

Every operation comes in two flavors – a synchronous one (e.g. GetStringValue) and an asynchronous one (e.g. GetStringValueAsync).

  • Asynchronous methods use the standard IJSRuntime and work in any render mode (Interactive Server, Interactive WebAssembly, Auto).
  • Synchronous methods require IJSInProcessRuntime, which is only available in the Interactive WebAssembly render mode. Calling them in any other context throws an InvalidOperationException.

The synchronous API is convenient in a WebAssembly app, where it avoids the asynchronous state machine:

@inject ILocalStorageService LocalStorage

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        // OnAfterRender runs only on the (WebAssembly) client once the component is interactive,
        // which guarantees the IJSInProcessRuntime required by the synchronous methods is available.
        if (firstRender)
        {
            if (LocalStorage.TryGetStringValue("greeting", out var value))
            {
                // ...
            }
        }
    }
}

Storing objects (JSON) #

Beside the raw string methods (SetStringValue/GetStringValue), you can store any value with SetValue<TValue> and read it back with GetValue<TValue>. The value is serialized to/from JSON using System.Text.Json.

public record UserPreferences(string Theme, bool CompactMode);

// store
await LocalStorage.SetValueAsync("preferences", new UserPreferences("dark", CompactMode: true));

// read
var (success, preferences) = await LocalStorage.TryGetValueAsync<UserPreferences>("preferences");

// read (throws StorageKeyNotFoundException when the key is missing)
var preferences2 = await LocalStorage.GetValueAsync<UserPreferences>("preferences");

Configuring JSON serialization #

You can configure the default JsonSerializerOptions used by SetValue/GetValue (and their async variants) during the service registration. The options are used whenever no JsonSerializerOptions are passed to the individual method call.

builder.Services.AddHavitBlazorStorage(new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});

// or via the options delegate:
builder.Services.AddHavitBlazorStorage(options =>
{
    options.JsonSerializerOptions = new JsonSerializerOptions
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };
});

Individual calls can always override the configured options by passing their own JsonSerializerOptions.

API overview #

All members are available in both synchronous and asynchronous (suffixed with Async) variants on IBrowserStorage:

  • SetStringValue / GetStringValue – store and read a raw string value.
  • TryGetStringValue – read a string value without throwing when the key is missing.
  • SetValue<T> / GetValue<T> – store and read a JSON-serialized object.
  • TryGetValue<T> – read a JSON-serialized object without throwing when the key is missing.
  • Remove – remove a single key.
  • Clear – remove all keys from the storage area.
  • GetLength – the number of keys stored.
  • GetKeyByIndex / TryGetKeyByIndex – enumerate the keys by their zero-based index.

Error handling #

The storage methods can throw the following exceptions:

  • StorageNotAvailableException – the storage area is not accessible (e.g. disabled or blocked by the browser privacy settings).
  • StorageKeyNotFoundException – thrown by GetStringValue/GetValue when the key is not present.
  • StorageIndexOutOfRangeException – thrown by GetKeyByIndex when the index is out of range.
  • InvalidOperationException – thrown by the synchronous methods when IJSInProcessRuntime is not available (i.e. not in the Interactive WebAssembly render mode).
  • ArgumentException / ArgumentNullException – thrown when invalid arguments are provided (e.g. key is null/whitespace, or a stored value is null).
try
{
    var token = await LocalStorage.GetStringValueAsync("token");
    // ...
}
catch (StorageKeyNotFoundException)
{
    // the key has not been stored yet
}
catch (StorageNotAvailableException)
{
    // storage is disabled/blocked in the browser
}
An unhandled error has occurred. Reload 🗙