Kebechet.Blazor.IndexedDB
1.0.0
Prefix Reserved
dotnet add package Kebechet.Blazor.IndexedDB --version 1.0.0
NuGet\Install-Package Kebechet.Blazor.IndexedDB -Version 1.0.0
<PackageReference Include="Kebechet.Blazor.IndexedDB" Version="1.0.0" />
<PackageVersion Include="Kebechet.Blazor.IndexedDB" Version="1.0.0" />
<PackageReference Include="Kebechet.Blazor.IndexedDB" />
paket add Kebechet.Blazor.IndexedDB --version 1.0.0
#r "nuget: Kebechet.Blazor.IndexedDB, 1.0.0"
#:package Kebechet.Blazor.IndexedDB@1.0.0
#addin nuget:?package=Kebechet.Blazor.IndexedDB&version=1.0.0
#tool nuget:?package=Kebechet.Blazor.IndexedDB&version=1.0.0
Blazor.IndexedDB
Key/value and collection storage over IndexedDB for Blazor WebAssembly, Blazor Server and .NET MAUI Blazor Hybrid. One database, any number of named stores, values moved as UTF-8 JSON bytes, an atomic collection replace, and an in-memory fallback so a WebView without a working IndexedDB never breaks the app.
Live storybook - interactive stories for every feature.
Why
WebView storage is not a given. WKWebView can leave window.indexedDB undefined for a whole session after a background launch, and both iOS and Android may evict web storage under pressure. Most IndexedDB wrappers assume the API is there and throw when it is not - which, at startup, means a broken app. This package treats "no storage" as a normal state:
- Nothing throws. A failed read is a miss, a failed write is dropped and logged, and
GetDriver()tells you which mode the session runs in. - Bytes, not strings. Values cross the interop as
byte[]produced by yourJsonSerializerOptions, so a source-generated context keeps trimming and AOT safe under MAUI on iOS, and a 5k-record cache does not marshal a multi-megabyte string. - Atomic collection replace.
ReplaceCollectionswaps a store's whole collection in one transaction; a reader never sees half old, half new. - One database, many stores. Stores are namespaces inside one IndexedDB database, so listing, replacing or clearing a store is a single key range, not a schema migration.
Installation
dotnet add package Kebechet.Blazor.IndexedDB
Register the service. Pass your own JsonSerializerOptions when you use a source-generated context:
using Kebechet.Blazor.IndexedDB;
builder.Services.AddIndexedDbStorage(options =>
{
options.DatabaseName = "my-app";
options.JsonSerializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = MyJsonContext.Default,
};
});
No script tag: the JS module is imported on demand from the package's static web assets.
Usage
@inject IIndexedDbStorage _storage
@code {
private const string SettingsStore = "settings";
private const string ExercisesStore = "exercises";
private async Task Example()
{
// key/value
await _storage.Write(SettingsStore, "theme", new ThemeSettings { IsDark = true });
var theme = await _storage.Read<ThemeSettings>(SettingsStore, "theme"); // null when missing
var hasTheme = await _storage.Exists(SettingsStore, "theme");
var keys = await _storage.GetKeys(SettingsStore);
await _storage.Remove(SettingsStore, "theme");
// a store's collection - replaced atomically, read back as a whole
await _storage.ReplaceCollection(ExercisesStore, exercises);
var cached = await _storage.ReadCollection<Exercise>(ExercisesStore); // empty when missing
// values that must survive a crash right after the write
await _storage.Write("auth", "refreshToken", token, StorageDurability.Strict);
// know what this session runs on
var driver = await _storage.GetDriver(); // IndexedDb, Memory or Unavailable
await _storage.Clear(ExercisesStore);
}
}
API
| Member | Behaviour |
|---|---|
Read<T>(store, key) |
Value under the key, or default when missing or unreadable. |
Write<T>(store, key, value, durability) |
Upserts the value. |
Remove(store, key, durability) |
Deletes the entry; a missing key is not an error. |
Exists(store, key) |
true when the entry exists; false when storage is unavailable. |
GetKeys(store) |
Keys of the store's key/value entries. The collection is not a key. |
ReadCollection<T>(store) |
The store's collection, or an empty list. |
ReplaceCollection<T>(store, values, durability) |
Replaces the collection in one transaction; key/value entries are untouched. |
Clear(store, durability) |
Removes the store's entries and its collection. |
GetDriver() |
IndexedDb, Memory (database could not be opened; data lives until the page unloads) or Unavailable (module could not be loaded). |
StorageDurability maps to the IndexedDB transaction durability hint: Relaxed (default, faster) or Strict (flushed before the transaction completes).
Semantics worth knowing
- A store holds key/value entries and, independently, one collection.
Clearwipes both;ReplaceCollectiontouches only the collection. - The driver is decided once per session, on the first operation. A database that stops opening mid-session also switches the session to memory - the same rule, applied late.
- An existing database with the configured name is reused as it is; the package's own object store is added to it with a one-version bump, so it can share a database with other code.
- Every failure is logged through
ILogger<IndexedDbStorage>: errors per failed operation, one warning when a session falls back to memory. Route those to your error reporting to learn how often real users run degraded.
Testing
dotnet test src/Blazor.IndexedDB.slnx # the .NET service: serialization, degradation, lifecycle
cd tests/Blazor.IndexedDB.JsTests && npm ci && npm test # the JS module: IndexedDB path over fake-indexeddb, and the memory fallback
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.AspNetCore.Components.Web (>= 10.0.0)
-
net6.0
- Microsoft.AspNetCore.Components.Web (>= 6.0.36)
-
net7.0
- Microsoft.AspNetCore.Components.Web (>= 7.0.20)
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.0)
-
net9.0
- Microsoft.AspNetCore.Components.Web (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 666 | 8/24/2026 |
Initial release. One IndexedDB database, any number of named stores; Read/Write/Remove/Exists/GetKeys per key plus ReadCollection/ReplaceCollection per store, the replace running as a single transaction. Values travel as UTF-8 JSON bytes serialized with caller-supplied JsonSerializerOptions, so a source-generated context keeps trimming and AOT safe. When the browser exposes no working IndexedDB (seen in WKWebView after background launches) the same API keeps working over an in-memory store for the session, and GetDriver reports which mode the session runs in. No operation throws on a storage failure.