Casey Baggz
Cerberus Admin
This release massively improves Signals performance along with minor React updates.
Here is a brief overview of what's new:
The React updates contain some new features and stability fixes for the Button along with a breaking change for the toaster options regarding subtle notifications.
We've added some new sizes to the Button recipe along with making the styling overall more stable when used in complex flex layouts.
New Button size range: xs up to 2xl
Default size: lg (this is visually the old medium)
Fix: Button no longer allows a flex parent to shrink its width. Likewise, buttons now have built in SVG size management when using non-strict width SVGs.
Checkout the Button documentation for more details.
Due to changes in the toaster engine there is a minor breaking change for subtle notifications (default notifications are un-affected).
Migration PathL:
type name without the -subtle prefix.usage: 'subtle' within the ToastOptions object.Additionally, all deprecated APIs have been removed. These warnings have been around for almost a year - it's time.
While this update is not ideal, it does open the door to new Toast-related opportunities in the future.
View the Notification documentation for more details.
Here are some high-level improvements made:
Tooltip now accepts a portal prop to determine how to render contentuseStatefulCollection now filters by label in addition to valuebutton, icon, iconButton, and menuTriggerAs of v1.3.0 - Signals are almost as performant as SolidJS and querys outperform Tanstack Query on multiple levels!
In this next phase of Signals, we have improved the scheduling algorithm along with the improving the query caching and query API. This has created a massive increaese in query performance which we will see later. 🚀
Across the board, we have updated a lot of doc pages and demos to provide more information around Signals: what they are, why they are important, and why you should be using them over React state/existing fetching designs.
Primitives are the building blocks to Cerby Signals. They can be called in any scope and used however you want. There are no boundaries with primitives.
Let's checkout the new APIs in this release.
The onCleanup primitive is an essential memory-management tool within the Cerberus Signals reactivity system. It allows you to register a callback function that will execute right before a reactive context (like createEffect or createComputed) re-evaluates, or when that context is permanently destroyed.
In this example, we ensure a timer is cleared when it's parent effect is destroyed.
View the onCleanup docs
The untrack primitive allows you to read the current value of a signal inside a reactive context (like createEffect, createComputed, or a React render phase) without subscribing to that signal.
This prevents unwanted re-evaluations, React infinite loops, and over-subscribing to state changes.
In this example, we update a signal and show the latest result (tracked) and what the untracked version is.
View the untrack docs
Along with the signal engine, we have also improved the query cache and event algorithms which have allowed us to not only improve performance but expand the APIs.
When you call createQuery you are essentially creating a "Query Factory" to be used later to perform the fetch in however way you may want.
Creating a query now requires two things:
Depending on how you want to use the factory depends on how you execute the query. You can use it in any shape, but for the sake of this article, we will discuss component usage only.
Nothing changes here. Just pass the factory in the useQuery hook and have the data ready before the return hits.
In an SSR environment, you execute the factory's raw fetcher directly, bypassing the reactive cache. Then, you pass that data down to your Client Components to "hydrate" or seed the Cerberus cache, ensuring the client doesn't double-fetch on mount.
This is the standard SSR pattern for React.
Expose the raw, stateless fetcher function from your factory. You simply await it like a standard asynchronous function.
On the client side, use the initialData property. If the Cerberus cache is empty, it will instantly seed the cache with the server's data, skipping the <Suspense> boundary entirely.
Cerberus queries also support streaming data via Async Generators. This is powerful if you are using an LLM API or your local API supports data streaming.
View the createQuery docs
We have added more options to the createMutation primitive which now includes optimistic updates. This means that you can have "real-time" UI while fetches automagically fetch in the background and update the query state when resolved.
In this example, we provide optimistic updates via the onMutate/setQueryData APIs which instantly update the UI while the query is run in the background.
If your optimistic update is incorrect, the UI will naturally resolve itself when the query completes.
View the createMutation docs
When you want to use a unqiue instance of a global Store but do not need a Context API - use the useStore hook.
View the useStore docs
Now let's talk about the good stuff - performance improvements with hard data.
Cerby Signals were already performing at 0(1) but that is never good enough. Here is our own before/after with our engine upgrades:
| Benchmark | Original (v1.2.1) | Current (v1.3.0) | Result |
|---|---|---|---|
| Deep Dependency | 318.17 µs | 226.83 µs | ~28% Faster |
| Wide Fan-Out | 2.26 ms | 3.17 ms | ~40% Slower |
| Diamond Problem | 323.05 µs | 458.60 µs | ~40% Slower |
While this seems like a loss - it is still lightning fast compared to the upgraded ownership and versioning we have built into subscriptions to be more reliable.
This is completely normal. Frameworks like SolidJS and Vue go through this exact pendulum swing. We traded raw, bare-metal speed for enterprise-grade memory safety (cleaning up event listeners and aborting network requests).
Even with the "regression," processing 10,000 effects in 3 milliseconds is still blazing fast and easily clears the 16ms frame budget for 60FPS UI updates.
Our next planned update should improve these metrics even further.
Now let's look at the real gains from this side effect: querys.
| Benchmark | Original (v1.2.1) | Current (v1.3.0) | Delta |
|---|---|---|---|
| Cache Retrieval (10k) | 8.88 ms | 1.68 ms | ~5.2x Faster |
| Retrieval Memory | 20.61 MB | 20.72 KB | ~99.9% Less Memory |
| Invalidation Sweep | 868.52 ns | 151.80 ns | ~5.7x Faster |
While our internal onCleanup addition added a tiny microsecond tax to the raw scheduler engine, the query data layer you built on top of it is functioning at absolute peak efficiency. We traded ~80 nanoseconds in key hashing for a massive 500% speed increase in data retrieval and a 99.9% reduction in memory allocation.
Now let's put the rubber to the road and go head to head with Tanstack Query.
We ran a head-to-head performance benchmark comparing the Cerberus Signals data layer against the industry standard, TanStack Query Core. The tests were run in Bun (v1.2.16) on an Apple M3 Pro, measuring both raw execution speed and the memory footprint (p75/p99) of the Garbage Collector.
By leveraging an O(1) signal-based memory map instead of heavy class instantiations, Cerberus completely eliminates massive amounts of memory overhead while doubling the speed of standard operations.
| Benchmark Scenario | Metric | TanStack Query Core | Cerberus Signals | The Cerberus Advantage |
|---|---|---|---|---|
| Cache Retrieval(10,000 concurrent reads) | Speed | 3.02 ms | 1.53 ms | 2x Faster |
| Memory | 59.62 KB | 639.72 bytes | 98.9% Less Memory | |
| Cache Invalidation (Stale data sweep) | Speed | 472.77 ns | 146.41 ns | 3.2x Faster |
| Memory | 12.29 bytes | 3.39 bytes | 72.4% Less Memory |
Note: Memory measurements represent the p75/p99 allocations recorded during the benchmark loops.
The Takeaway: When scaling to thousands of reactive components, memory allocation becomes the primary bottleneck for UI thread frame rates. Cerberus Signals delivers enterprise-grade concurrency features (like Race-Condition Locks and React 19 Hydration) while consuming fractional kilobytes of memory compared to the megabytes allocated by traditional context-based providers.
This is another great release introducing some beneficial tools and features.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
There is no "I" in Cerber-"US"
To upgrade to this release, you can install the latest version of Cerberus React:
npm run up:cerberuspnpm run up:cerberusdeno run npm:up:cerberusbun run up:cerberusCount: 0
Untracked: 0
Render Count: 1
Count: 0
Render Count: 1
User d817d2ab-7258-4ef8-80fd-9045bd7994fe