I Shipped...

18 May 2026 (5PRs)

a fix for the jazz-tools CLI silently doing nothing when run through pnpm

When you install Jazz using pnpm, it creates a symlink (a shortcut) pointing to the actual CLI program. The CLI checks whether it’s the entry point by comparing file paths, but the symlinked path and the real path are different strings, so it thought it wasn’t the entry point and quietly exited without doing anything — making schema pushes and migrations appear to succeed while actually no-oping. I fixed it by resolving both paths to their real locations before comparing them, so the CLI now dispatches correctly whether called directly or through pnpm’s symlinks.

a Vue 3 todo example for Jazz

I added a new example app showing how to build a local-first todo list using Vue 3 and Jazz. It mirrors the existing React, Svelte, and TypeScript examples, so Vue developers now have a reference starting point too. The example also includes browser-based integration tests using Vitest.

dropping Node.js 20 support in favour of Node.js 22

Node.js 20 reached end-of-life, meaning it no longer receives security updates. I bumped the minimum required Node.js version to 22.12 and took the opportunity to clean up backwards-compatibility workarounds scattered across 19 files that only existed to support older Node versions.

LocalFirstAuth framework hooks in the Jazz starter templates

The Jazz starter templates had a lot of repetitive boilerplate for setting up authentication. I updated the SvelteKit, React, and Next.js starters to use the new LocalFirstAuth reactive class and framework-specific hooks, which means cleaner code and auth state that automatically rebuilds when something changes — no manual page reload needed.

reactive LocalFirstAuth support for Svelte and Vue

Jazz already had a React hook for LocalFirstAuth — a way to handle user login in local-first apps (apps that work offline and sync data in the background). I added matching reactive integrations for Svelte and Vue, so developers using those frameworks can use LocalFirstAuth without having to wire up their own reactivity. I also fixed a crash that occurred when the module was imported on the server side, and made sign-out properly propagate across all open tabs.

15 May 2026 (2PRs)

a Dashboard link to the Jazz docs site navigation

I added a link to the Jazz Dashboard in the top navigation bar of the docs site, alongside the existing Blog and Docs links. It opens in a new tab and makes it easier for users to jump between the documentation and the dashboard without having to hunt for the URL.

an animated diagram showing how Jazz syncs data across tiers in the docs

I replaced a static diagram on the “how sync works” docs page with an animated SVG that shows how writes flow between global sync servers, edge servers, and local clients in real time. To keep things readable, I made each writer get a randomly generated color that’s always different enough from its previous color. I also refactored the diagram building blocks into a shared module so future diagrams can reuse the same code.

14 May 2026 (1PR)

a type-level fix for hopTo destination row type inference

When you use hopTo() to jump from one table to a related table in a Jazz query, TypeScript was wrongly inferring that the result still had the type of the original table instead of the destination. I fixed this by properly threading the destination table’s type information through the hopTo method, so now TypeScript correctly knows the shape of the data you’re working with after the hop. The runtime behaviour was already correct — this was a purely type-level fix.

13 May 2026 (8PRs)

a cleanup of the example apps' docs, tests, and feature parity

Several Jazz example apps had accumulated inconsistencies: some still used Playwright for testing while others had moved to Vitest browser mode, and their docs and feature sets had drifted out of sync. I migrated the remaining auth examples to Vitest browser mode and fixed the various inconsistencies to bring the examples back to parity.

a fix for deep query subscriptions losing reactivity

When you subscribed to Jazz data with deeply nested relationships — like posts that include authors that include teams — changes more than one level deep wouldn’t trigger a UI update. The subscription system only registered top-level tables as dependencies and missed the deeper ones. I fixed the graph compilation step to walk all nesting levels and register every nested table, so deep mutations correctly invalidate subscriptions.

a fix for the browser benchmark suite being hidden from CI

Two separate regressions had quietly broken the browser benchmark tests so they weren’t running in CI at all. A recent refactor changed how database insert results are structured, breaking the seed step, and a separate issue prevented the test files from being discovered. I fixed both problems so the benchmark suite runs correctly again.

a rebuilt world-tour example with a custom globe and Vue tests

The world-tour example app used a third-party map library to display a globe, which was a heavy external dependency. I replaced it with a hand-rolled canvas-based dot globe made from 50,000 Fibonacci sphere points, dropped the dependency entirely, added the example to the monorepo workspace, and included Jazz+Vue integration tests alongside it.

a TypeScript type fix for the SvelteKit plugin in strict mode

Adding the Jazz SvelteKit plugin to vite.config.ts with TypeScript strict mode enabled would fail to compile because the plugin’s ssr.external option was typed as string[], but Vite’s actual type is true | string[]. I widened the type to match what Vite expects, so it compiles cleanly under strict mode.

an RSS feed for the Jazz docs blog

The Jazz docs blog didn’t have an RSS feed, so there was no easy way for people to subscribe and be notified of new posts. I added an RSS 2.0 feed at /rss.xml built from all blog posts sorted newest-first, and registered it in the site layout so browsers and feed readers can auto-discover it.

markdown serving for AI agents from the Jazz docs

AI coding assistants work better with plain text than with HTML. I added a proxy to the Jazz docs site that serves any page as clean markdown when a request includes an Accept: text/markdown header or a .md suffix in the URL, making it easy for AI agents to read the docs directly.

three new TypeScript-only starter templates

Jazz’s starter templates were all React-based, making it hard to see how Jazz works without a UI framework in the way. I added three new TypeScript-only starters (ts-localfirst, ts-hybrid, and ts-betterauth) that use plain DOM manipulation inside Jazz subscription callbacks, so the core API is front and centre.

7 May 2026 (3PRs)

a fix for broken polyfills in the Expo integration that were crashing third-party libraries

The Jazz Expo integration included three polyfills (bits of code that patch in missing browser features) for standard web networking types — but these were actually harmful. If they ran before React Native had set up its own network globals, they’d overwrite those globals with undefined, breaking third-party libraries like Clerk. I removed the three useless polyfills while keeping the two that do real work.

an interactive lens diagram to the migrations documentation

Jazz uses “lenses” to handle schema migrations — they define how old data gets transformed when you change your data model. I added an animated, interactive diagram to the documentation that visualises how data moves through the lens chain, making the concept much easier to grasp than text alone.

dropping an unnecessary fetch polyfill from the Jazz Expo integration

The Expo (React Native) integration for Jazz included a custom fetch polyfill that swapped out the built-in network library for one from expo/fetch. It was originally added to support reading response bodies as streams, but Jazz never actually does that — all fetch calls just grab the whole response at once with .json() or .text(), and real-time sync runs over WebSockets anyway. I removed the polyfill entirely, simplifying the code and preventing potential conflicts with third-party libraries that also try to override the global fetch.

6 May 2026 (1PR)

a fix for a React Native UI freeze caused by synchronous database queries

The jazz-rn library was running database queries by blocking the JavaScript thread, which froze the UI during any database access and could also cause deadlocks when the server needed to respond before a query could complete. I converted the query function to be async, so it runs off the JavaScript thread and returns a Promise, keeping the app responsive while waiting for data.