Skip to content

About

Modern browser storage (localStorage + sessionStorage) for Blazor WebAssembly and client-side render modes. Typed, async, lightweight.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Build Status NuGet License: MIT

D20Tek.Blazor.BrowserStorage

A modern, lightweight .NET library that provides typed, asynchronous access to the browser's localStorage, sessionStorage, and cookie APIs for Blazor WebAssembly and client-side render modes. Built using JavaScript interop, this library eliminates the need for manual serialization, raw JS calls, or string-based key management. It offers a clean, strongly-typed C# API with full dependency injection support.

This package was inspired by Blazored.LocalStorage/SessionStorage. Once I realized that it was deprecated and removed from NuGet.org, I needed my own implementation to use across my Blazor apps. While building on the basic functionality, D20Tek.Blazor.BrowserStorage provides additional features and a more modern, flexible API. The most significant difference is that GetAsync<T> returns a StorageResult<T> instead of throwing an exception when a key is not found, allowing for more graceful handling of missing keys.

Table of Contents

Why This Library Exists

Blazor does not provide a built‑in way to access localStorage or sessionStorage from .NET. The only way to use browser storage is to call JavaScript manually through IJSRuntime, which leads to several problems:

  • You must write JS interop boilerplate for every read/write.
  • Storage access becomes stringly‑typed, error‑prone, and repetitive.
  • You have to handle JSON serialization yourself.
  • There’s no clean way to expose storage as a typed .NET service.
  • Previously used Blazored.LocalStorage package for these purposes, but that has been deprecated and no longer available.

For a framework that encourages strong typing, DI, and clean architecture, browser storage ends up feeling like a low‑level workaround. And this library exists to fix that.

D20Tek.Blazor.BrowserStorage provides:

  • A fully typed storage API
  • A clean async interface
  • Zero‑boilerplate JSON handling
  • A simple, DI‑friendly .NET service
  • Support for localStorage, sessionStorage, and cookies
  • A modern API designed for Blazor WebAssembly and Blazor SSR

It gives Blazor developers a first‑class, standard way to use browser storage, without having to touch JavaScript.

Features

  • Typed, async API: Read and write any serializable .NET type with generic GetAsync<T> and SetAsync<T> methods. No manual JSON handling required.
  • Result-based reads and writes: GetAsync<T> returns a StorageResult<T> and SetAsync<T> / RemoveAsync / ClearAllAsync return a StorageResult, each with an IsSuccess flag and an optional ErrorMessage. Missing keys, corrupt values, quota-exceeded, and blocked-storage conditions surface as results rather than exceptions.
  • Availability probing: IsAvailableAsync detects whether browser storage is usable (private mode, disabled site data, quota exhausted) and caches the result.
  • localStorage, sessionStorage, and cookies: Full support for all three browser storage mechanisms through ILocalStorageService, ISessionStorageService, and ICookieStorageService, which all share the same typed, result-based API shape.
  • Configurable cookie attributes: CookieStorageOptions controls DefaultExpiration, Path, Domain, SameSite (via CookieSameSiteMode), and Secure for every cookie written through ICookieStorageService.
  • Bulk operations: SetMultipleAsync and RemoveMultipleAsync extension methods for batch read/write scenarios, with fail-fast semantics that return the first failing StorageResult.
  • Key prefix namespacing: Configure a prefix string (for example, "myapp_") that is automatically prepended to all keys, preventing collisions between multiple applications or modules sharing the same origin.
  • Change notifications: Subscribe to the Changed event on either service to receive StorageChangedEventArgs whenever a value is added, modified, or removed.
  • Configurable JSON serialization: Provide custom JsonSerializerOptions for scenarios that require specific naming policies, converters, or formatting.
  • Flexible service lifetimes: Register services as Scoped (default), Singleton, or Transient to match your application's architecture.
  • Lightweight and focused: No external dependencies beyond the standard Microsoft.JSInterop and Microsoft.Extensions packages.

Supported Platforms

Target Framework Status
.NET 9.0 Supported
.NET 10.0 Supported

This library is designed for Blazor WebAssembly and Blazor client-side interactive render modes. It is not intended for server-side Blazor (Blazor Server), where Microsoft's built-in ProtectedLocalStorage and ProtectedSessionStorage should be used instead.

Installation

Install the package via the .NET CLI:

dotnet add package D20Tek.Blazor.BrowserStorage

Or via the NuGet Package Manager in Visual Studio:

Install-Package D20Tek.Blazor.BrowserStorage

Quick Start Guide

1. Register services in Program.cs

Register both localStorage and sessionStorage services with a single call:

using D20Tek.Blazor.BrowserStorage;

var builder = WebAssemblyHostBuilder.CreateDefault(args);

builder.Services.AddBrowserStorage();

await builder.Build().RunAsync();

If you only need one of the two storage types, you can register them individually:

builder.Services.AddLocalStorage();
// or
builder.Services.AddSessionStorage();

Cookie storage is registered separately with AddCookieStorage, which also accepts an optional delegate for configuring the cookie attributes:

builder.Services.AddCookieStorage(
    configureCookie: cookieOptions =>
    {
        cookieOptions.DefaultExpiration = TimeSpan.FromDays(365);
        cookieOptions.Path = "/";
        cookieOptions.SameSite = CookieSameSiteMode.Lax;
        cookieOptions.Secure = true;
    });

2. Inject the service into a component

@inject ILocalStorageService LocalStorage
@inject ISessionStorageService SessionStorage
@inject ICookieStorageService CookieStorage

Or in a code-behind file:

[Inject]
private ILocalStorageService LocalStorage { get; set; } = default!;

3. Read and write values

// Write a value
var writeResult = await LocalStorage.SetAsync("username", "Alice");
if (!writeResult.IsSuccess)
{
    // Storage may be full, disabled, or otherwise unavailable.
    Console.WriteLine(writeResult.ErrorMessage);
}

// Read a value
var result = await LocalStorage.GetAsync<string>("username");
if (result.IsSuccess)
{
    Console.WriteLine(result.Value); // "Alice"
}

Cookies use the same typed API, so reading and writing a cookie looks identical apart from the injected service:

// Write a cookie using the configured default expiration
await CookieStorage.SetAsync("consent", new ConsentPreferences { Analytics = true });

// Read it back
var consent = await CookieStorage.GetAsync<ConsentPreferences>("consent");
if (consent.IsSuccess)
{
    Console.WriteLine(consent.Value.Analytics); // True
}

Two cookie-specific behaviors are worth noting:

  • Only script-accessible (non-HttpOnly) cookies can be read or written, because the service is implemented through document.cookie.
  • Browsers do not raise a native change event for cookie writes made in other tabs, so ICookieStorageService.Changed only fires for writes and removals performed through the same service instance.

For guidance on when to choose cookies over localStorage, and for the full set of cookie options, see the Cookie Storage Guide.

Usage and Configuration

For detailed usage instructions covering all storage operations (reading, writing, removing, checking keys, enumerating, bulk operations, and change notifications) as well as configuration options (key prefixing, custom JSON serialization, and service lifetimes), see the Detailed Getting Started Guide.

API Reference

For a complete reference of all public interfaces, methods, events, extension methods, and types, see the API Reference.

Sample Applications

The repository includes three sample Blazor WebAssembly applications that demonstrate the library's features in realistic scenarios:

A settings and preferences dashboard that uses localStorage to persist visual preferences (theme, accent color, and font family) across browser sessions, and sessionStorage to track dismissal of a "What's New" banner within the current tab.

Features demonstrated: GetAsync<T>, SetAsync<T>, ContainsKeyAsync, ClearAllAsync, key prefix namespacing, and the Changed event.

An interactive tech trivia game with 100 questions across .NET, Azure, and Windows categories. The application uses localStorage for persistent data such as player profiles, high scores, and game statistics, and sessionStorage for current quiz state with recovery on page refresh.

Features demonstrated: Complex object serialization, StorageResult<T> for first-time player detection, SetMultipleAsync for batch result saving, RemoveMultipleAsync for session cleanup, GetKeysAsync and LengthAsync for storage statistics, and ClearAllAsync for data reset.

A cookie-consent banner that uses ICookieStorageService to persist a visitor's consent decision (analytics/marketing categories) for a full year across browser restarts - a scenario where a cookie is a better fit than localStorage because the same decision could also be read by a server-rendered page on the same domain.

Features demonstrated: AddCookieStorage with CookieStorageOptions (DefaultExpiration, Path, SameSite), typed GetAsync<T> / SetAsync<T>, RemoveAsync to reset consent, and the Changed event keeping the banner and a settings page in sync.

Testing

For unit and component tests, install the companion package D20Tek.Blazor.BrowserStorage.Testing. It ships in-memory implementations of ILocalStorageService, ISessionStorageService, and ICookieStorageService plus bUnit BunitContext extensions so you can test components that depend on browser storage without spinning up a real browser or mocking IJSRuntime by hand.

dotnet add package D20Tek.Blazor.BrowserStorage.Testing
using var ctx = new BunitContext();
ctx.AddBrowserStorage(o => o.KeyPrefix = "app_");
ctx.GetLocalStorage().Seed("prefs", new UserPreferences { Theme = "Dark" });

var cut = ctx.Render<PreferencesPanel>();
cut.Find("button.reset").Click();

Assert.False(ctx.GetLocalStorage().Snapshot.ContainsKey("app_prefs"));

Cookie-backed components are registered and inspected the same way through AddCookieStorage and GetCookieStorage:

using var ctx = new BunitContext();
ctx.AddCookieStorage();
ctx.GetCookieStorage().Seed("consent", new ConsentPreferences { Analytics = true });

var cut = ctx.Render<ConsentBanner>();

See docs/testing.md for the full guide — seeding, Snapshot inspection, availability simulation, and cross-tab Changed event simulation. For the complete list of public types, methods, and extensions in the testing package, see the Testing API Reference.

Migration from Blazored.LocalStorage

If you are migrating from the Blazored.LocalStorage and Blazored.SessionStorage packages, see the Migration Guide for a detailed comparison of methods, return types, and registration patterns.

Used by

Used in my Blazor projects and internal apps.

License

This project is licensed under the MIT License. See the LICENSE file for details.

About

Modern browser storage (localStorage + sessionStorage) for Blazor WebAssembly and client-side render modes. Typed, async, lightweight.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages