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.
- Features
- Supported Platforms
- Installation
- Quick Start Guide
- Usage
- Configuration
- API Reference
- Cookie Storage Guide
- Sample Applications
- Testing
- Migration from Blazored.LocalStorage
- License
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.
- Typed, async API: Read and write any serializable .NET type with generic
GetAsync<T>andSetAsync<T>methods. No manual JSON handling required. - Result-based reads and writes:
GetAsync<T>returns aStorageResult<T>andSetAsync<T>/RemoveAsync/ClearAllAsyncreturn aStorageResult, each with anIsSuccessflag and an optionalErrorMessage. Missing keys, corrupt values, quota-exceeded, and blocked-storage conditions surface as results rather than exceptions. - Availability probing:
IsAvailableAsyncdetects 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, andICookieStorageService, which all share the same typed, result-based API shape. - Configurable cookie attributes:
CookieStorageOptionscontrolsDefaultExpiration,Path,Domain,SameSite(viaCookieSameSiteMode), andSecurefor every cookie written throughICookieStorageService. - Bulk operations:
SetMultipleAsyncandRemoveMultipleAsyncextension methods for batch read/write scenarios, with fail-fast semantics that return the first failingStorageResult. - 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
Changedevent on either service to receiveStorageChangedEventArgswhenever a value is added, modified, or removed. - Configurable JSON serialization: Provide custom
JsonSerializerOptionsfor 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.
| 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.
Install the package via the .NET CLI:
dotnet add package D20Tek.Blazor.BrowserStorageOr via the NuGet Package Manager in Visual Studio:
Install-Package D20Tek.Blazor.BrowserStorage
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;
});@inject ILocalStorageService LocalStorage
@inject ISessionStorageService SessionStorage
@inject ICookieStorageService CookieStorageOr in a code-behind file:
[Inject]
private ILocalStorageService LocalStorage { get; set; } = default!;// 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 throughdocument.cookie. - Browsers do not raise a native change event for cookie writes made in other tabs, so
ICookieStorageService.Changedonly 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.
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.
For a complete reference of all public interfaces, methods, events, extension methods, and types, see the API Reference.
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.
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.Testingusing 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.
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 in my Blazor projects and internal apps.
This project is licensed under the MIT License. See the LICENSE file for details.