Repository navigation
A Relational Database In The Browser, With TinyJoin
The new persister-tinyjoin module provides the TinyJoinPersister, which saves and loads a Store to and from a TinyJoin database - a tiny, worker-first relational database that runs entirely in the browser:
import {createStore} from 'tinybase';
import {createTinyJoinPersister} from 'tinybase/persisters/persister-tinyjoin';
import {create} from 'tinyjoin';
const tinyJoin = await create('opfs://my-app');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createTinyJoinPersister(store, tinyJoin, 'my_tinybase');
await persister.save();
console.log((await tinyJoin.query('SELECT * FROM my_tinybase')).rows);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]
await persister.destroy();
await tinyJoin.close();Like the other database Persisters, it supports both the JSON and tabular modes, and it follows TinyJoin's own table subscriptions rather than polling for changes. TinyJoin's SQL dialect is deliberately bounded, so read the createTinyJoinPersister function for the two boundaries worth knowing about.
SQL Server and Azure SQL, via mssql
The new persister-mssql module provides the MsSqlPersister, which binds to a SQL Server database with the mssql module. Since Azure SQL Database and Azure SQL Managed Instance speak the same protocol, the same Persister works against all three:
import {connect} from 'mssql';
import {createStore} from 'tinybase';
import {createMsSqlPersister} from 'tinybase/persisters/persister-mssql';
const msSqlPool = await connect('Server=localhost,1433;Database=tinybase');
const msSqlStore = createStore().setTables({pets: {fido: {species: 'dog'}}});
const msSqlPersister = await createMsSqlPersister(
msSqlStore,
msSqlPool,
'my_tinybase',
);
await msSqlPersister.save();
console.log(
(await msSqlPool.request().query('SELECT * FROM my_tinybase;')).recordset,
);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]
await msSqlPersister.destroy();
await msSqlPool.request().query('DROP TABLE IF EXISTS my_tinybase;');
await msSqlPool.close();The Persister takes a connection pool that you have already configured, so it stays out of the way of how you authenticate. That matters most on Azure, where Microsoft recommends passwordless access for hosted applications: build the pool with an azure-active-directory-default authentication type and the Persister needs to know nothing about it.
This release supports the JSON serialization mode, for both a Store and a MergeableStore. Tabular mapping may follow.
Automatic loading works differently here than it does for PostgreSQL. There is no equivalent of LISTEN and NOTIFY that is available on every flavor of SQL Server, so the Persister adds a rowversion column to its table and polls it. SQL Server maintains that column itself on every insert and update, so changes made by other writers are still picked up, without needing Service Broker, Change Tracking, or triggers.
A huge thank you to Peter Skoglund (@nltgpeterskoglund) for building all of this - the Persister, the T-SQL dialect handling, the polling, and its tests.
libSQL, With Pooled Connections
The LibSqlPersister now expects v0.18 of the @libsql/client module, which changed the way a local database is opened. A client now keeps a pool of connections rather than a single one, and every command borrows a connection for its duration and returns it afterwards - rolling back any transaction still open on it.
A BEGIN statement issued on its own therefore no longer survives the command that sent it, and so the Persister now runs each write as a transaction session for local file and in-memory databases, just as it always has for remote ones. It previously avoided them, because a session used to open a second connection - and, for an in-memory database, that meant a second and empty database. In v0.18 such a client has a single connection, so that is no longer a concern.
Your own code does not need to change, but your @libsql/client dependency should be v0.18 or later.
A Fuller Agent Skill
The official build-with-tinybase agent skill now ships inside the npm package, at node_modules/tinybase/skills/build-with-tinybase/, so a coding agent can read it locally without fetching it from the web. It remains published at https://tinybase.org/skills/build-with-tinybase/SKILL.md.
It also gained three reference files aimed at the mistakes agents actually make: the import subpath and MergeableStore capability of every Persister, Synchronizer and Schematizer; the lifecycle rules for Persisters and Synchronizers, including WebSocket paths versus channel Ids; and a complete Cloudflare Durable Object recipe covering the Wrangler bindings and migration tag, server-side persistence, and authentication.
The repository is now a Claude Code plugin marketplace, so the skill can be installed directly:
/plugin marketplace add tinyplex/tinybase
/plugin install tinybase@tinybaseA Site You Can Navigate Without A Mouse
The documentation site is now keyboard-navigable throughout. Every page starts with a 'Skip to content' link that becomes visible once focused, the navigation and main content are marked up as labelled landmarks, and focus is now visibly styled wherever it lands.
The color theme toggle cycles automatic, dark, and light as before, but it now announces which mode is active and which one activating it will select, so it is usable from a screen reader rather than by its icon alone.
Breaking Changes in v10.0
There are four, and each affects a single Persister. Three are removals, two of which v9.7 said were coming.
The CR-SQLite Persister Has Been Removed
The persister-cr-sqlite-wasm module has been removed. Unlike the two below, it was not deprecated in v9.7, so this one arrives without notice. The cr-sqlite project it bound to has not had a release since 2024.
It was the odd one out among the Persisters in any case, since it could only ever save a Store and not a MergeableStore, so it could not back a synchronized setup. If you want CRDT-backed sync, TinyBase's own MergeableStore does it natively - see the Using A MergeableStore guide - and the YjsPersister and AutomergePersister remain for the two other external CRDT libraries. For SQLite in a browser, the SqliteWasmPersister and the TinyJoinPersister are both actively supported.
The @vlcn.io/crsqlite-wasm package is no longer an optional peer dependency of TinyBase, so you can drop it from your project when you upgrade.
The sqlite3 Persister Has Been Removed
The persister-sqlite3 module has been removed. The sqlite3 module that it bound to is no longer maintained - its own v6 release marks the repository as such - and SQLite now ships inside Node.js.
The SqliteNodePersister replaces it directly, and the move is close to a rename:
// Before
import {Database} from 'sqlite3';
import {createSqlite3Persister} from 'tinybase/persisters/persister-sqlite3';
const persister = createSqlite3Persister(
store,
new Database(':memory:'),
'my_tinybase',
);// After
import {DatabaseSync} from 'node:sqlite';
import {createSqliteNodePersister} from 'tinybase/persisters/persister-sqlite-node';
const persister = createSqliteNodePersister(
store,
new DatabaseSync(':memory:'),
'my_tinybase',
);Both modes and MergeableStore support carry over unchanged. The one behavioral difference is automatic loading: sqlite3 reported changes as they happened, whereas node:sqlite does not signal them, so the SqliteNodePersister polls instead. If you need Node.js versions that predate the built-in node:sqlite module, reach for the BetterSqlite3Persister, which polls in the same way.
The sqlite3 package is no longer an optional peer dependency of TinyBase, so you can drop it from your project when you upgrade.
The ElectricSQL Persister Has Been Removed
The persister-electric-sql module has been removed, as the v9.7 deprecation said it would be. The electric-sql module that it bound to was deprecated by its authors, has not been published since 2024, and has been superseded by a rebuilt sync engine with a different architecture that does not map onto the Persister interface.
If you are working with PostgreSQL, the PgPersister, PostgresPersister and PglitePersister are all actively supported, and the last of those runs in a browser. The electric-sql package is no longer an optional peer dependency of TinyBase, so you can drop it - and its own wa-sqlite requirement - from your project when you upgrade.
libSQL Client v0.18
The persister-libsql module now requires v0.18 or later of the @libsql/client module, up from v0.17. Update that dependency when you upgrade. The 'libSQL, With Pooled Connections' section above describes what changed and why, and no TinyBase code of your own needs to change.
Everything Else
Nothing else has been removed or renamed, and no existing signature has changed. Setting the three removed Persisters aside, the TinyBase API surface in v10.0 is v9.7 plus the TinyJoinPersister and the MsSqlPersister.