SignalDB and the case for a database that lives in the browser
SignalDB is a reactive, local-first JavaScript database designed for modern web applications. It combines signal-based reactivity with powerful local data management and real-time synchronization capabilities.
At a glance
- What is it?
- SignalDB is a client-side JavaScript database that keeps documents in local storage, exposes a MongoDB-like API, and re-runs queries when the underlying signals change. It is a good fit for offline-tolerant web interfaces, and it is not a replacement for a server-authoritative database.
- Who is it for?
- SignalDB fits front-end teams that want an offline-tolerant interface, instant local reads and a query API they already recognise, and it fits projects that need to mix a local reactive copy with a hosted backend such as the ones exercised in the repository's own examples. It is the wrong choice when the server must remain the single source of truth, when you need server-side aggregation or transactions, or when nobody owns the browser's copy of the data.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Documents live in the browser, and the server is optional context
SignalDB's premise is that the place where an interface reads and writes data is the browser, not a remote service. The project describes it as a client-side database optimised for modern web applications, one that provides an optimistic UI for responsive interfaces, a MongoDB-like interface for familiarity, and TypeScript support for type safety. The target user is a web developer who wants offline-tolerant behaviour and instant local reads without hand-writing a cache layer, sync queue and reactive binding for every screen. The topics attached to the repository make the intent clear: database, local-first, optimistic-ui, reactive-programming, signals, synchronization, typescript. What the project is not is a networked database server. Nothing in the documentation suggests you point your app at a SignalDB instance and it serves queries over the wire. It sits in the browser tab, and the question of what happens to data when a server is involved is answered by adapters you choose yourself.
Collections, cursor queries and the $set update operator
The API is deliberately familiar. You create a Collection, insert a document, and get an identifier back. Updates use MongoDB-style operators, and queries return a cursor rather than a plain array:
import { Collection } from '@signaldb/core'
const Posts = new Collection()Writes follow the same familiar shape, with updateOne and removeOne taking a filter and a document modifier such as $set, and queries return a cursor rather than a plain array:
const cursor = Posts.find({})
// returns an array with all documents in the collection
// reruns automatically in a reactive context
console.log(cursor.fetch())The cursor lines are the important ones. fetch() returns the current documents as an array, and the README notes it reruns automatically in a reactive context. That is the whole reactivity story in a sentence: a query is a live view rather than a snapshot, and the signal graph decides when it recomputes. The update shape, with $set inside a document modifier, is a deliberate choice to reuse MongoDB muscle memory, which helps if you have written queries against a server database before. Filters are plain objects such as { id: postId }, so building predicates dynamically is straightforward.
A cursor that re-runs is the mechanism behind the optimistic UI claim
The optimistic UI the project advertises follows from that cursor behaviour rather than from a separate transaction engine. When a component reads cursor.fetch() inside a reactive context, the value is recomputed when the underlying signals change, so a local insert or update shows up in the interface without a round trip to a server and without an explicit subscription call. Local writes are applied to the client copy first, which is what makes an interface feel instant on a slow connection. What the README does not describe is the other half of the picture. There is no account of how a write that the server rejects is reconciled, no mention of a conflict policy when two devices change the same document, and no rollback mechanism in the documentation. That absence is not a minor gap. Optimistic updates are easy when the server accepts everything, and hard exactly when it does not, so for anything beyond a prototype the behaviour of a rejected write is the first thing to look for in the reference documentation rather than to assume.
Core package, separate reactivity adapters, no framework in the core
The repository is a npm workspace, and the package layout makes the architecture explicit. The root package.json lists workspaces for packages/integrations/*, packages/storage-adapters/*, packages/reactivity-adapters/*, packages/devtools/* and packages/base/*. The split is the design. The core collection and query logic lives on its own, framework-agnostic base, and framework support is delivered as adapters. The project names the frameworks it supports: Angular, React, Solid, Svelte and Vue.js, each with a guide in the documentation. Adapters for reactivity are listed among popular reactive libraries, and the framework-agnostic design is presented as making the library suitable for projects of any scale. For a team already on one of those frameworks, the practical consequence is that you install the core plus a matching reactivity adapter rather than importing framework code into your own. Devtools are a separate workspace too, which suggests debugging and inspection are an optional layer you add when a collection is hard to reason about.
Storage adapters decide what a local copy is worth
Where the documents actually live is not fixed by the core. The documentation speaks of versatile local data persistence with support for various storage providers, and the repository provides a workspace of storage adapters, which means the persistence layer is a pluggable choice rather than an implementation detail. That choice has direct consequences for behaviour you can expect. A storage adapter backed by an origin-local store survives a reload and works offline; one that talks to a hosted backend inherits that backend's latency and availability, and the optimistic behaviour then matters much more. The repository's examples/ directory is the best guide to the range, since it contains working apps for appwrite, firebase, supabase and a replication-http case, alongside a shared examples/src/ directory. A replication example is the interesting one for evaluation, because replication is where the local-first claims meet a network. The README itself does not enumerate the storage providers it supports, so treat the adapter list in the packages directory, and the examples, as the authoritative source.
Getting the first collection running takes one install and one import
Installation is a single package. The README gives the install command for the core, and the framework adapter is added separately if you use one:
$ npm install @signaldb/coreFrom there the shortest useful path is the usage snippet above: import Collection, construct a collection, insert a document, and query it. Nothing in the documentation requires a server, a schema definition or a migration step before the first insert. The package installs from npm, and the workspace's own development flow is visible in the root scripts: vitest runs the tests, vitest bench runs benchmarks, eslint lints, typedoc builds the API reference, vitepress builds the documentation site, and node .scripts/build-all.js builds every package. The preversion script chains lint, test and build, so a release is gated on all three. For a contributor, the entry points are the examples directory and the package workspaces, and the documentation sources live in docs/ with a CNAME pointing at the published site.
What SignalDB does not do: server queries, transactions, and any single source of truth
There are clear cases where this is the wrong tool, and they all follow from the client-side design. If your application needs aggregation, joins across large collections, or transactional guarantees that must hold on a server, a browser-resident document store is the wrong layer, and the project makes no claim to provide them. The same applies to access control: the documentation does not describe an authorisation model, because enforcement naturally lives in the backend you sync with. Then there is the operational side. The last push to the default branch was on 2026-10-19, and recent releases include v1.8.1 on 2026-03-17, v1.8.0 on 2026-03-13 and v1.7.2 on 2026-01-07, so releases are arriving at a steady pace under an MIT licence, copyright Max Nowack. That makes the project safe to try, and it also means the surface area you depend on may still shift between minor versions.
Editorial conclusion
SignalDB fits front-end teams that want an offline-tolerant interface, instant local reads and a query API they already recognise, and it fits projects that need to mix a local reactive copy with a hosted backend such as the ones exercised in the repository's own examples. It is the wrong choice when the server must remain the single source of truth, when you need server-side aggregation or transactions, or when nobody owns the browser's copy of the data. Before adopting it, confirm which storage adapter you will use, since the README lists support for various storage providers without naming them, and read the documentation for how writes that the server rejects are handled, because the README does not describe that conflict path. If that behaviour is undocumented for your data model, keep the server authoritative and use SignalDB only as a cache.
Frequently asked questions
How do I install SignalDB and create my first collection?
Install the core package with npm install @signaldb/core, then import Collection from '@signaldb/core' and construct a collection, for example const Posts = new Collection(). Documents are then added with the insert method, and queries return a cursor whose fetch method re-runs in a reactive context.
Does SignalDB work with React, Vue and other frameworks?
The project ships adapters for popular reactive libraries, naming Angular, React, Solid, Svelte and Vue.js, with a guide for each in the documentation. The core is framework-agnostic, so the reactivity adapter is what connects a collection to your components.
Where is the data stored in SignalDB, and is it available offline?
Persistence is handled by storage adapters, and the documentation speaks of support for various storage providers without naming them in the README. The examples directory shows working integrations for appwrite, firebase, supabase and a replication-http case, which is the place to check what a given adapter does offline.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/maxnowack-signaldb)Was this useful?