Detailed Getting Started Guide

This document provides detailed usage instructions and configuration options for D20Tek.Blazor.BrowserStorage. For a quick-start guide, see the Quick Start section in the main README.

Table of Contents

Usage

Reading Values

The GetAsync<T> method returns a StorageResult<T> rather than throwing an exception when a key is not found. This design allows callers to handle missing keys gracefully without try/catch blocks.

// Simple types
var result = await LocalStorage.GetAsync<int>("visit-count");
int visits = result.IsSuccess ? result.Value : 0;

// Complex objects
var profileResult = await LocalStorage.GetAsync<UserProfile>("user-profile");
if (profileResult.IsSuccess && profileResult.Value is not null)
{
	var profile = profileResult.Value;
}

The StorageResult<T> record struct contains two properties:

Property Type Description
IsSuccess bool Indicates whether the key was found and the value was deserialized successfully.
Value T? The deserialized value, or the default value of T if the key was not found.

Writing Values

The SetAsync<T> method serializes the provided value to JSON and stores it under the specified key. Any serializable .NET type can be stored, including primitive types, collections, and complex objects.

// Primitive types
await LocalStorage.SetAsync("theme", "dark");
await LocalStorage.SetAsync("font-size", 16);
await LocalStorage.SetAsync("notifications-enabled", true);

// Complex objects
var profile = new UserProfile
{
	Name = "Alice",
	CreatedDate = DateTimeOffset.UtcNow
};
await LocalStorage.SetAsync("user-profile", profile);

// Collections
var scores = new List<ScoreEntry> { new() { Score = 95, Date = DateTimeOffset.UtcNow } };
await LocalStorage.SetAsync("high-scores", scores);

Removing and Clearing Data

Remove a single key, or clear the entire storage area. Both methods return a StorageResult for consistency with SetAsync:

// Remove a specific key
await LocalStorage.RemoveAsync("username");

// Clear ALL keys in this browser storage area (destructive, area-wide).
// This removes every key in localStorage/sessionStorage for the current origin,
// including keys written by other libraries. The configured KeyPrefix is NOT
// honored - enumerate GetKeysAsync + RemoveAsync to scope the delete instead.
await LocalStorage.ClearAllAsync();

Handling Failures

All mutation methods (SetAsync, RemoveAsync, ClearAllAsync) return a StorageResult with IsSuccess and an optional ErrorMessage. This lets you surface storage problems (quota exceeded, disabled site data, private-mode restrictions) without exception handling:

var result = await LocalStorage.SetAsync("user-profile", profile);
if (!result.IsSuccess)
{
	// Show a message, fall back to in-memory state, or retry.
	logger.LogWarning("Storage write failed: {Error}", result.ErrorMessage);
}

Similarly, GetAsync<T> populates ErrorMessage when the failure is not a simple missing key (for example, when the stored JSON is corrupt or storage is unavailable).

The bulk extensions (SetMultipleAsync, RemoveMultipleAsync) use fail-fast semantics: the first failing item's result is returned and remaining items are not attempted.

Checking for Keys

Use ContainsKeyAsync to check whether a key exists without reading its value:

bool exists = await LocalStorage.ContainsKeyAsync("user-profile");
if (!exists)
{
	// First-time visitor: create default profile
}

Enumerating Storage

Retrieve the number of stored keys or list all key names:

// Get the total number of keys
int count = await LocalStorage.LengthAsync();

// Get all key names
IReadOnlyList<string> keys = await LocalStorage.GetKeysAsync();
foreach (var key in keys)
{
	Console.WriteLine(key);
}

Checking Storage Availability

Use IsAvailableAsync to detect whether the underlying browser storage is usable before performing operations. Storage can be unavailable when the browser is in a restricted private mode, when the user has blocked site data, or when quota has been exhausted. The result is cached after the first check.

if (!await LocalStorage.IsAvailableAsync())
{
	// Fall back to in-memory state or notify the user.
}

When storage is unavailable, read operations return empty/failure results and write operations return a failure result with an explanatory ErrorMessage rather than throwing.

Bulk Operations

The SetMultipleAsync and RemoveMultipleAsync extension methods allow batch operations in a single logical call. These methods iterate over the provided items and perform individual storage operations for each entry. Both return a StorageResult and fail fast on the first item that cannot be written or removed.

// Write multiple values at once
var items = new List<KeyValuePair<string, object>>
{
	new("high-scores", updatedScores),
	new("games-played", gamesPlayed),
	new("last-played", DateTimeOffset.UtcNow)
};
var bulkResult = await LocalStorage.SetMultipleAsync(items);
if (!bulkResult.IsSuccess)
{
	logger.LogWarning("Bulk save stopped: {Error}", bulkResult.ErrorMessage);
}

// Remove multiple keys at once
await SessionStorage.RemoveMultipleAsync(["quiz-state", "current-streak", "timer"]);

Change Notifications

Both ILocalStorageService and ISessionStorageService expose a Changed event that fires whenever a value is written or removed through the service. This is useful for updating UI elements in response to storage changes.

LocalStorage.Changed += (sender, args) =>
{
	Console.WriteLine($"Key '{args.Key}' changed from '{args.OldValue}' to '{args.NewValue}'");
	StateHasChanged();
};

The StorageChangedEventArgs class provides the following properties:

Property Type Description
Key string The storage key that was modified.
OldValue object? The previous value, or null if the key is new.
NewValue object? The new value, or null if the key was removed.

Configuration

Key Prefixing

Configure a key prefix to namespace all storage keys and prevent collisions with other applications or libraries sharing the same browser origin:

builder.Services.AddBrowserStorage(options =>
{
	options.KeyPrefix = "myapp:";
});

With this configuration, a call to SetAsync("theme", "dark") will store the value under the key "myapp:theme" in the browser. The prefix is applied transparently and does not affect the keys used in your application code.

Custom JSON Serialization

Provide custom JsonSerializerOptions to control how values are serialized and deserialized:

builder.Services.AddBrowserStorage(options =>
{
	options.JsonOptions = new JsonSerializerOptions
	{
		PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
		WriteIndented = false,
		DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
	};
});

When no custom options are provided, the library uses JsonSerializerDefaults.Web, which applies camelCase property naming and case-insensitive deserialization by default.

Service Lifetime

By default, services are registered with a Scoped lifetime, which is the standard for Blazor WebAssembly. You can change the lifetime to Singleton or Transient if your application requires it:

using Microsoft.Extensions.DependencyInjection;

// Register as Singleton
builder.Services.AddBrowserStorage(lifetime: ServiceLifetime.Singleton);

// Register with options and a custom lifetime
builder.Services.AddLocalStorage(
	options => options.KeyPrefix = "app:",
	lifetime: ServiceLifetime.Transient
);
An unhandled error has occurred. Reload 🗙