SqliteWasmBlazor 0.9.4-pre
dotnet add package SqliteWasmBlazor --version 0.9.4-pre
NuGet\Install-Package SqliteWasmBlazor -Version 0.9.4-pre
<PackageReference Include="SqliteWasmBlazor" Version="0.9.4-pre" />
<PackageVersion Include="SqliteWasmBlazor" Version="0.9.4-pre" />
<PackageReference Include="SqliteWasmBlazor" />
paket add SqliteWasmBlazor --version 0.9.4-pre
#r "nuget: SqliteWasmBlazor, 0.9.4-pre"
#:package SqliteWasmBlazor@0.9.4-pre
#addin nuget:?package=SqliteWasmBlazor&version=0.9.4-pre&prerelease
#tool nuget:?package=SqliteWasmBlazor&version=0.9.4-pre&prerelease
SqliteWasmBlazor
True filesystem-backed SQLite with full EF Core support for Blazor WebAssembly.
A real SQLite engine (official sqlite-wasm) runs in a Web Worker on OPFS with synchronous access handles; your app talks to it through a complete ADO.NET provider and EF Core. Data survives refreshes, restarts and browser updates. No server, no emulated filesystem, no manual serialization.
Try the live demo — installable as a PWA for offline use.
| Solution | Storage | Persistence | EF Core |
|---|---|---|---|
| InMemory | RAM | none | full |
| IndexedDB | IndexedDB | yes | limited — no SQL |
| SQL.js | IndexedDB | yes | none — manual serialization |
| besql | Cache API | yes | partial — emulated filesystem |
| SqliteWasmBlazor | OPFS | yes | full |
Install
dotnet add package SqliteWasmBlazor --prerelease
Optional at-rest encryption and its drop-in UI are separate packages:
dotnet add package SqliteWasmBlazor.Crypto --prerelease # encrypted VFS
dotnet add package SqliteWasmBlazor.Crypto.UI --prerelease # auth / encryption panels
Upgrading from an earlier pre-release? Breaking changes are listed per version in the Changelog.
Quick Start
Program.cs
using SqliteWasmBlazor;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddDbContextFactory<TodoDbContext>(options =>
{
var connection = new SqliteWasmConnection("Data Source=TodoDb.db");
options.UseSqliteWasm(connection);
});
builder.Services.AddSqliteWasm();
builder.Services.AddSqliteWasmDbContext<TodoDbContext>();
var host = builder.Build();
await host.RunAsync();
Then place the initializer once, in your layout:
<SqliteWasmDatabaseInitializer/>
Program.cs only registers. <SqliteWasmDatabaseInitializer/> renders nothing;
after the first render it starts the worker and applies pending migrations for
every context declared with AddSqliteWasmDbContext<T>(). A migration that
cannot be applied — the schema on disk disagrees with the migrations in the
assembly — reports SCHEMA_INCOMPATIBLE with SQLite's reason, and the remedy is
a reset: delete the database, ISqliteWasmInitializer.Reset(), InitializeAsync().
The base package reports and never renders — it carries no UI framework. Read
IDbInitializationStatus, or register an IDbInitNotifier with
AddDbInitNotifier<T>() to be told of MIGRATING, READY and every failure as
it happens. SqliteWasmBlazor.Crypto.UI ships <DatabaseInformationAlert/>, which
renders those states and offers the reset through the host's IHostRecoveryService.
Initializing after the app renders is what lets a long migration be reported instead of freezing a blank page — and it is the only way an encrypted pool can be opened at all, since its key comes from a WebAuthn ceremony that needs a UI.
Apps deployed on a sub-path must set the base href explicitly:
builder.Services.AddSqliteWasm(o => o.BaseHref =
new Uri(builder.HostEnvironment.BaseAddress).AbsolutePath);
Use it
@inject IDbContextFactory<TodoDbContext> DbFactory
@code {
private async Task SaveTodo(TodoItem todo)
{
await using var db = await DbFactory.CreateDbContextAsync();
db.TodoItems.Update(todo);
await db.SaveChangesAsync(); // persisted to OPFS
}
}
Your DbContext is an ordinary EF Core context — migrations, LINQ, Include, relationships and
decimal arithmetic all work as usual.
Public API
ISqliteWasmDatabaseService (via DI) covers database management outside of EF Core. Every file
path streams; none of them holds a database in managed memory.
public interface ISqliteWasmDatabaseService
{
bool CanCancelQueries { get; } // a service worker carries cancels to the running statement
Task<IReadOnlyList<string>> ListDatabasesAsync(CancellationToken ct = default);
Task<bool> ExistsDatabaseAsync(string databaseName, CancellationToken ct = default);
Task DeleteDatabaseAsync(string databaseName, CancellationToken ct = default);
Task RenameDatabaseAsync(string oldName, string newName, CancellationToken ct = default);
Task CloseDatabaseAsync(string databaseName, CancellationToken ct = default);
Task ExportDatabaseToStreamAsync(string databaseName, Stream destination,
CancellationToken ct = default);
Task ExportDatabaseToDownloadAsync(string databaseName, string filename,
CancellationToken ct = default);
Task ExportDatabasesToDownloadAsync(IReadOnlyList<string> databaseNames,
string filename, CancellationToken ct = default);
Task ImportDatabaseFromStreamAsync(string databaseName, Stream stream, long size,
Func<string, CancellationToken, ValueTask>? validateImported = null,
CancellationToken ct = default);
Task ImportDatabasesFromStreamAsync(Stream envelopeStream, long envelopeSize,
Func<string, CancellationToken, ValueTask>? validateImported = null,
CancellationToken ct = default);
Task<int> ImportRowsAsync(string databaseName, byte[] data,
CancellationToken ct = default);
}
Imports park what they replace, run the incoming file past an optional validateImported
check, and re-run the host's migrations before the import counts; a refusal restores the
previous files byte-identically.
Cancelling a CancellationToken always ends the wait; whether it also stops the running
statement depends on a service worker. One importScripts of
_content/SqliteWasmBlazor/sqlite-wasm-cancel.sw.js and a call to
handleSqliteWasmCancel(event) from its fetch and message listeners is the whole
integration; CanCancelQueries says whether a session has it. See
Cancelling Queries.
| Type | Purpose |
|---|---|
SqliteWasmConnection / Command / DataReader / Parameter / Transaction |
ADO.NET provider for direct SQL |
IDbInitializationStatus |
Initialization state and errors |
IDbInitNotifier / AddDbInitNotifier<T>() |
Initialization states pushed to the host as they happen; the host writes the sentences |
ISqliteWasmInitializer |
InitializeAsync() (idempotent) for a page that renders before the layout; Reset() before re-initializing |
MessagePackFileHeaderV2 / SchemaHashGenerator |
Header of an ImportRowsAsync payload and the schema hash it carries |
PoolImportResult |
Outcome of a raw .db import |
SchemaMismatchException |
Thrown by ValidateImportedSchemaAsync; carries MissingTables |
PoolOperationRejectedException |
Typed precondition refusal, carries Reason |
Everything else — worker bridge, VFS — is internal.
Optional: at-rest encryption
SqliteWasmBlazor.Crypto adds an encryption layer to the same VFS. Without a registered key it
falls through to byte-for-byte vendor SAHPool behavior.
- Every 4 096-byte page is sealed with ChaCha20-Poly1305, with the AEAD bound to
(versionTag, dbPath, slotIndex)so relocated or cross-database pages fail authentication. - The key is derived from a passkey via the WebAuthn PRF extension — no password, no key file.
- Unlock is verified (slot-0 AEAD probe + manifest MAC), not silent.
- Encrypted whole-pool export (
.eds) wraps the key for a recipient X25519 pubkey. SqliteWasmBlazor.Crypto.UIships drop-in RxBlazorV2 panels (en + de), requiresRxBlazorV2.MudBlazor1.3.2+.- Machine-checked: 3 Tamarin theories, 74 lemmas, all verified.
Details: Encrypted VFS · Security · Formal models
Documentation
| Topic | Description |
|---|---|
| Architecture | Worker-based architecture and technical details |
| ADO.NET Usage | Using the provider without EF Core, transactions |
| Advanced Features | Migrations, FTS5 search, JSON collections, logging, cancelling queries, moving databases in and out |
| Multi-Database | Multiple databases, cross-database references |
| Bulk Import/Export | V2 format, multi-part export, delta sync |
| Encrypted VFS | At-rest encryption and threat model |
| Recommended Patterns | Multi-view pattern, data initialization |
| FAQ | Common questions and browser support |
| Changelog | Release notes, breaking changes, version history |
Browser Support
Chrome/Edge 108+, Firefox 111+, Safari 16.4+ — every browser from 2023 on, including iOS/iPadOS Safari and Android Chrome. All support OPFS with synchronous access handles.
Large databases move on mobile. Exports stage through an OPFS file the browser saves from disk; imports stream into the worker one chunk at a time. Neither heap ever holds the whole file, so a multi-hundred-megabyte database transfers on a phone. iOS/iPadOS has the tightest memory budget of the four engines — building for it is what makes the paths safe everywhere.
Installed to the Home Screen there is no downloads folder, so WebKit presents an export on a full-screen OS sheet ("Open in …") instead of saving it. The file streams at any size and the sheet's share menu saves to Files — worth telling your users, since it looks like a failure.
Related Projects
| Project | Description |
|---|---|
| RxBlazorV2 | Reactive framework for Blazor on R3: source-generated observable models, commands and components. |
| BlazorPRF | WebAuthn-PRF encryption primitives — absorbed into this repo as SqliteWasmBlazor.Crypto. |
Together: persistent local storage with optional at-rest encryption, plus reactive state management. Coming next: CryptoSync — end-to-end encrypted multi-device delta sync with per-row permissions and a relay that never sees plaintext.
Project Status
Pre-1.0. The public API is deliberately small and has been stable in practice, but broader real-world feedback is needed before committing to long-term guarantees.
Shipped: ADO.NET provider · OPFS SAHPool · EF Core migrations · FTS5 · multi-database · V2 worker-side bulk import/export · memory-flat streamed export/import · query cancellation · at-rest encryption with passkey-derived keys · drop-in auth/encryption UI · Tamarin-verified crypto lifecycle.
Next: stable NuGet release · CryptoSync (E2E encrypted delta sync) · server-side delta generation.
This is a non-commercial hobby project maintained in spare time — no fixed release cycle. "Hobby" refers to time, not craftsmanship: it is developed with test coverage and attention to code quality. It grows through bug reports, feature requests and pull requests.
Contributing
Issues, bug reports and feature discussions are always welcome. For code we use an issue-first policy: open an issue and agree on the approach before submitting a PR. Trivial fixes can be sent directly. See CONTRIBUTING.md.
Credits
Author: bernisoft · License: MIT (Copyright © 2025 bernisoft), see LICENSE
Built with SQLite / sqlite-wasm, EF Core, MessagePack and MudBlazor.
If you find this useful, please star the repository.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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
- MessagePack (>= 3.1.8)
- Microsoft.AspNetCore.Components (>= 10.0.12)
- Microsoft.EntityFrameworkCore (>= 10.0.12)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.12)
- Microsoft.EntityFrameworkCore.Sqlite (>= 10.0.12)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.12)
- Microsoft.Extensions.DependencyInjection (>= 10.0.12)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.12)
- Microsoft.Extensions.DependencyModel (>= 10.0.12)
- Microsoft.Extensions.Options (>= 10.0.12)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.12)
- R3 (>= 1.3.1)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on SqliteWasmBlazor:
| Package | Downloads |
|---|---|
|
Mythetech.Framework.WebAssembly
WebAssembly-specific components for building blazor applications |
|
|
BlazorPRF.Persistence
Ready-to-use persistence layer for BlazorPRF applications. Provides trusted contacts, invitations, and settings storage using EF Core with SQLite. |
|
|
BBH.SqliteWasmBlazor
A Blazor WebAssembly library for SQLite integration in browser history applications |
|
|
SqliteWasmBlazor.Crypto
Plane 2 engine for SqliteWasmBlazor: PRF-keyed at-rest encryption on top of the plain SQLite engine. Encrypted VFS lifecycle, WebAuthn-PRF key derivation, secure key cache, signing service. Add SqliteWasmBlazor.Crypto.UI for default auth/lock/unlock panels. |
|
|
SqliteWasmBlazor.Crypto.UI
Base-plane RxBlazorV2 + MudBlazor panels for SqliteWasmBlazor: WebAuthn-PRF authentication / registration, database boot-error alert, session-expired popover. Hosts wire IPrfAuthenticator / ISessionAuthenticator / IDatabaseResetService seams. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 0.9.4-pre | 130 | 9/13/2026 | |
| 0.9.3-pre | 829 | 8/23/2026 | |
| 0.9.1-pre | 890 | 5/17/2026 | |
| 0.9.0-pre | 632 | 4/25/2026 | |
| 0.8.5-pre | 852 | 4/2/2026 | |
| 0.8.4-pre | 111 | 4/1/2026 | |
| 0.8.3-pre | 109 | 3/31/2026 | |
| 0.8.1-pre | 186 | 3/27/2026 | |
| 0.8.0-pre | 107 | 3/18/2026 | |
| 0.7.2-pre | 197 | 1/28/2026 | |
| 0.7.1-pre | 98 | 1/27/2026 | |
| 0.7.0-pre | 1,455 | 11/17/2025 | |
| 0.6.9-pre | 254 | 11/16/2025 | |
| 0.6.8-pre | 149 | 11/15/2025 | |
| 0.6.7-pre | 256 | 11/14/2025 | |
| 0.6.6-pre | 240 | 11/14/2025 | |
| 0.6.5-pre | 306 | 11/13/2025 | |
| 0.6.4-pre | 300 | 11/13/2025 | |
| 0.6.3-pre | 292 | 11/13/2025 | |
| 0.6.2-pre | 604 | 11/13/2025 |
- Full EF Core ADO.NET provider
- OPFS SAHPool persistence
- MessagePack binary protocol
- Complete migration support
- Custom EF Core decimal functions
- Dual SQLite instance architecture