Skip to content
Client v5: BLE, BLE Hosting, HTTP, Jobs - Linux, MacOS, & Blazor Support! Full AOT, RX on BLE only & MANY other features! Power up!

IndexedDB (Blazor WASM)

The Shiny.DocumentDb.IndexedDb package provides an IndexedDB-backed document store for Blazor WebAssembly applications. It implements the full IDocumentStore interface using JavaScript interop — no native dependencies, no file system access required.

NuGet package Shiny.DocumentDb.IndexedDb
  • Blazor WebAssembly apps that need client-side persistent storage
  • Offline-capable progressive web apps (PWAs)
  • Browser-based apps where you want the same document store API as your server/mobile code
Terminal window
dotnet add package Shiny.DocumentDb.IndexedDb
Changed files
  • MyApp/
MyApp.csproj
MyApp.csproj
1<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">23    <PropertyGroup>4        <TargetFramework>net10.0</TargetFramework>5        <Nullable>enable</Nullable>6        <ImplicitUsings>enable</ImplicitUsings>7        <MicrosoftVersion>10.0.9</MicrosoftVersion>8        <ShinyClientVersion>5.0.0</ShinyClientVersion>9        <ShinyControlsVersion>1.0.1-beta-0129</ShinyControlsVersion>10        <ShinyExtensionsLocalizationVersion>2.0.1</ShinyExtensionsLocalizationVersion>11        <ShinyExtensionsStoresVersion>5.1.1</ShinyExtensionsStoresVersion>12        <ShinyExtensionsReflectorVersion>5.1.1</ShinyExtensionsReflectorVersion>13        <ShinyExtensionsDIVersion>5.1.1</ShinyExtensionsDIVersion>14        <ShinyExtensionsBlazorHostingVersion>5.1.1</ShinyExtensionsBlazorHostingVersion>15        <ShinySpeechVersion>2.1.0</ShinySpeechVersion>16        <ShinyAiConversationVersion>1.0.0-beta-0050</ShinyAiConversationVersion>17        <ShinyDocumentDbVersion>9.1.0</ShinyDocumentDbVersion>18        <MediatorVersion>6.6.2</MediatorVersion>19    </PropertyGroup>2021    <ItemGroup>22        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="$(MicrosoftVersion)" />23        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="$(MicrosoftVersion)" PrivateAssets="all" />2425        <PackageReference Include="Shiny.DocumentDb.IndexedDb" Version="$(ShinyDocumentDbVersion)" />2627    </ItemGroup>2829</Project>
  1. Register with dependency injection in your Blazor WASM Program.cs:

    using Shiny.DocumentDb.IndexedDb;
    builder.Services.AddSingleton(new IndexedDbDocumentStoreOptions { DatabaseName = "MyAppDb" });
    builder.Services.AddSingleton<IDocumentStore, IndexedDbDocumentStore>();

    Or with full options:

    builder.Services.AddSingleton(new IndexedDbDocumentStoreOptions
    {
    DatabaseName = "MyAppDb",
    Version = 1
    }.MapTypeToStore<User>() // dedicated object store
    .MapTypeToStore<Order>("orders") // explicit store name
    );
    builder.Services.AddSingleton<IDocumentStore, IndexedDbDocumentStore>();
  2. Inject and use IDocumentStore in your components:

    @inject IDocumentStore Store
    @code {
    private List<User> users = new();
    protected override async Task OnInitializedAsync()
    {
    users = (await Store.Query<User>().ToList()).ToList();
    }
    private async Task AddUser()
    {
    var user = new User { Name = "Alice", Age = 25 };
    await Store.Insert(user);
    users.Add(user);
    }
    }
PropertyTypeDefaultDescription
DatabaseNamestring(required)The name of the IndexedDB database
Versionint1IndexedDB database version. Increment when adding new object stores
StoreNamestring"documents"Default object store for unmapped types
TypeNameResolutionTypeNameResolutionShortNameHow type names are stored
JsonSerializerOptionsJsonSerializerOptions?nullJSON serialization settings
UseReflectionFallbackbooltrueSet to false for AOT safety
LoggingAction<string>?nullDiagnostic callback

Similar to table-per-type mapping in SQL providers, you can map document types to dedicated IndexedDB object stores:

builder.Services.AddSingleton(new IndexedDbDocumentStoreOptions
{
DatabaseName = "MyAppDb"
}.MapTypeToStore<User>() // auto-derived store name
.MapTypeToStore<Order>("orders") // explicit store name
.MapTypeToStore<Sensor>(s => s.DeviceKey) // custom Id property
);
builder.Services.AddSingleton<IDocumentStore, IndexedDbDocumentStore>();

The IndexedDB provider supports the full IDocumentStore API:

  • Insert, Update, Upsert, BatchInsert
  • Get, Remove, Clear
  • SetProperty, RemoveProperty
  • GetDiff (JSON patch diffing)
  • Query<T>() fluent LINQ queries (Where, OrderBy, Paginate, Select, aggregates)
  • Count<T>()
  • CreateUnitOfWork() + SaveChanges() (operations are batched in a single IndexedDB transaction)
  • Raw SQL queriesQuery<T>(string whereClause) and QueryStream<T>(string whereClause) throw NotSupportedException. Use the LINQ-based Query<T>() overload instead.
  • Spatial queries — IndexedDB does not support R*Tree or GeoJSON indexes.

Under the hood, the provider:

  1. Opens an IndexedDB database with the configured name and version
  2. Creates object stores (with a typeName index) during the upgradeneeded event
  3. Stores documents as { key, id, typeName, data, createdAt, updatedAt } records
  4. Uses the composite key "{TypeName}:{Id}" as the IndexedDB key path
  5. Queries load all documents of a type via the typeName index, then apply LINQ predicates client-side

All IndexedDB operations are performed through a JavaScript ES module (shiny-indexeddb.js) bundled as a static web asset.

If you need SQL-level query features (raw WHERE clauses, JSON indexes, spatial queries), the existing SQLite provider is also WASM-compatible when paired with SQLitePCLRaw.bundle_wasm:

  • WAL pragma is automatically skipped on OperatingSystem.IsBrowser()
  • Spatial (R*Tree) is automatically disabled in the browser
  • Backup() is marked [UnsupportedOSPlatform("browser")]
  • Use Data Source=:memory: or Emscripten OPFS-mounted paths

The IndexedDB provider is lighter (no native WASM SQLite binary) and simpler for most client-side storage scenarios. Choose SQLite-in-WASM only when you need its advanced query capabilities.

When you add new type-to-store mappings, increment Version so that the upgradeneeded event fires and creates the new object stores:

opts.Version = 2; // was 1, now creating a new store
opts.MapTypeToStore<NewType>();

IndexedDB handles the schema migration automatically — existing data in other stores is preserved.

IndexedDB supports temporal historyMapTemporal<T> records every version to a {store}_history object store, read back via the ITemporalDocumentStore capability interface (History/AsOf/Restore/GetDiffBetween/AsOfAll/…). Resolve or cast to ITemporalDocumentStore; the methods are not on the base IDocumentStore.

Because temporal adds new object stores, bump Version when adding MapTemporal to an already-deployed database so the upgrade creates the history stores (a fresh database needs no change):

opts.Version = 2; // was 1 — schema upgrade creates the {store}_history object stores
opts.MapTemporal<Order>();