Optimistic Updates
Clicks feel instant. Syncing happens in the background. Your app is quick even when the network is not.
Your app should feel fast. Not fast-ish. Not fast-on-a-good-connection. Fast.
sp00ky makes every write feel immediate. You call a mutation, your data updates locally right then and there, and the screen re-renders on the next frame. The trip to the server happens quietly in the background. If the Wi-Fi is having a moment, your write is safe on the device and will send itself later. Your users never see a spinner because they clicked a button.
The old way, and why everyone dreads it
Most frameworks make you write optimism by hand, one mutation at a time. You click "post comment," and somewhere in the code you have to whisper to the cache: "pretend the comment is already there, in this list, and also in that counter, and also in the sidebar, and if the server says no, undo all of it." That logic gets big, gets fragile, and slowly drifts away from what the server actually does. Eventually a teammate changes one and not the other, and you find out about it on a Friday.
How sp00ky does it
You just write your queries and your mutations. sp00ky handles the rest.
- You subscribe to a query. sp00ky remembers it.
- You call a mutation. sp00ky updates your local data instantly.
- Every query affected by that change gets a fresh result, automatically.
- Your UI re-renders right away. No extra code, no manual cache updates.
- The server call goes out in the background. When it comes back, sp00ky lines everything up.
Behind the scenes there is a small query engine called the Streaming Processor that runs inside your browser. Its job is boring and important: it watches your active queries, and when data changes, it figures out which ones care and updates only those. You never have to talk to it.
One write, every view
This is the part that usually costs hours of component surgery in other frameworks. Add a new comment, and the comments list updates, the count updates, the "last reply" timestamp on the sidebar updates, the badge on the thread updates. All of it. Because sp00ky knows which queries depend on which data, one write covers every view that cared. You did not wire any of that.
Write the query once. Write the mutation once. Every affected view stays in sync, on its own.
How this compares
Most popular clients treat optimism as an afterthought bolted onto fetching. Here is how they ask you to express it, and where sp00ky takes a different approach.
| Framework | How you express optimism | Rollback on error | Scope of one update |
|---|---|---|---|
| Apollo Client | optimisticResponse + update functions | You write it | Per cache entry you patch |
| Relay | optimisticUpdater / optimisticResponse | You write it | Per record in the store |
| Replicache / Zero | Local JS mutator functions | Engine rebase | Whatever the mutator writes |
| RTK Query | onQueryStarted + updateQueryData | You write it | Per query you patch |
| sp00ky | Nothing. The query engine derives it | Engine rebase | Every query that depends on the table |
| SWR | mutate() with optimisticData | You write it | One query at a time |
| TanStack Query | onMutate + setQueryData | You write it | One query at a time |
Green check: the framework handles it for you. Amber bar: partial, you still write some code. Red cross: it lands on your desk.
Apollo, Relay, RTK Query, SWR, and TanStack Query all expect you to write the optimism by hand, once per mutation and once per query. Replicache and Zero move it into local mutator functions, which is a step up but still means you maintain a second version of your server logic in JavaScript. sp00ky makes it something the library does, not something you do.
Quick, on purpose
The Streaming Processor is written in Rust and runs in the browser as WebAssembly, which keeps it light on your main thread. It only recomputes the queries that actually care about a change, which keeps it light on your app. Proper benchmarks will follow in a later release, and we will update this page with numbers we trust enough to put our name on.
When the network goes sideways
- Offline: your write is safe on the device. When you reconnect, sp00ky sends it automatically.
- Server disagrees: the server's version wins and your UI quietly corrects itself.
- Shared counters or collaborative text: opt in to CRDT fields at the schema level when you want real merge semantics. Everything else stays simple.
- Errors: surface as recoverable errors you can show or retry. Never lost writes.
Ready to build something amazing?
Full docs, install guide, and API reference — all in one place.
Read the docs