a quick fix to address a security issue in Next
There was a security issue in Next due to an issue in React. I bumped our Next versions.
There was a security issue in Next due to an issue in React. I bumped our Next versions.
Our docs previously had a page showing how to use Jazz’s version control functionality with React. I updated our docs to show how it can be done across all our supported frameworks.
I’m quite proud of this one: I build a minimal Jazz example (a working to-do list with sync in less than 100 lines of code), and published it on our docs landing page.
Our nav was behaving strangely following an update designed to ensure the correct segments were always open. I fixed it.
In our React Native and Expo documentation, we had some references to outdated practices and unnecessary modules. I refactored the docs to remove these, and I also consolidated the polyfills into a single module which can be imported directly from Jazz.
In Jazz, you can use ‘selectors’ to avoid React re-rendering on every state update. However, the selector function itself still runs. Added tips on avoiding heavy computation inside selector functions, and how to extract that into a useMemo call outside the selector to take advantage of batching.
I proposed and launched an ‘Advent of Jazz’ initiative on our Discord. This PR adds a prominent link through a banner for December.
Normally, Jazz will throw if it cannot complete a resolve query as requested by the user (for example because the user doesn’t have access to the data or it doesn’t actually exist). Users can override this by specifying $onError: 'catch', which will instead return the rest of the query completed correctly with only the CoValue in question missing.
There was a bug in how this was implemented which I fixed.
Previously, our analytics only captured successful search. This improvement allows us to track unsuccessful searches.
In our docs, we have a hook which fetches the current framework, however, there were other bits of code around the codebase which do similar things, and the hook was becoming unwieldy and difficult to follow.
I refactored the useFramework hook to make it easier to use, and improve the separation of concerns on the homepage docs.
We had some small issues with the new vanilla docs because the snippets were created before the big refactoring of code snippets was merged. This PR brought them in line with the framework specific snippets.
Expo and React Native do not ship a TextDecoder implementation. This docs update explains how to polyfill for (huge) performance improvements in native apps.
When a Jazz app is uninstalled, all data is deleted except the login credentials on iOS which are persisted to the keychain. This can cause issues if the account is not synced and the user reinstalls the app.
We have an example chat app for all our supported frameworks. For our browser-based frameworks, this chat allows also uploading images to the chat, but our mobile apps did not.
I added the image upload functionality to the mobile framework examples.
Users can create lists using Jazz, but there’s no protection to stop users from inserting the same item multiple times into a CoList. For users wanting lists of unique items, I proposed and wrote up a pattern for using a CoRecord keyed on the CoValue ID.
This was a huge piece of work ahead of SyncConf 2025. I built a Jazz-based step sequencer in SvelteKit. Users can collaborate on an 8 beat grid to trigger various one-shot samples in a musical sequence.
The demo is aimed at iPad screen size, and includes primitive sync logic to make sure that devices sound in sync when played simultaneously. This was particularly challenging, and I eventually did it based on calculating clock skew roughly based on a heartbeat from the Jazz mesh. I would like to explore using Jazz as a WebRTC signalling channel.
The source for this is currently closed, but the demo can be accessed at https://jazz-groove.vercel.app/
At some point, I introduced a regression that was causing our homepage docs to enter into a recursive navigation loop calling useEffect multiple times. This quick PR fixed it.
I was quite pleased with the PR title on this one: “…infinite recursion: a fix to prevent…”
Twoslash is a code-highlighting and type-checking plugin which helps to make sure all our code samples are kept up to date. Unfortunately though, it causes our build times to be very slow.
I extracted all of our code samples into separate files, and developed a new way of integrating them into our docs. This led to huge speed wins, bringing our build times down by around 5 minutes per build (on Vercel), roughly 14 hours of compute per month.
It also brought even more significant savings in terms of development. Where previously, it took me (M4 Macbook Pro) 110s to compile the docs in dev mode, now it takes 7s, a speed increase of 15×, making it much easier to develop our homepage and our docs overall.
Currently, Jazz stores secrets in localStorage. This is accessible by client-side JavaScript, and so any untrusted code running in a developers’ app could allow the secret to be extracted. Although this is a required trade-off in order to avoid re-authenticating every single session, users are now warned about the risk of cross-site scripting, and advised to take steps to prevent it.
Jazz intelligently de-duplicates loads, meaning that each part of an application can load exactly what data it needs without worrying about what is loaded elsewhere in the app. The user will not pay a performance hit because only the extra data which is not already loaded will be fetched, and the existing loaded data will not be reloaded.
I was getting annoyed with Zed not showing any feedback when I tried to commit changes using Cmd+Enter. I made the button appear greyed out when there’s a commit operation pending.
I added some detail to our existing docs showing how users who are not using a framework (‘vanilla’ users) can get started with Jazz.
In our Performance Tips section, I added a section telling users that if they’re adding a lot of values, they can improve performance by inserting them simultaneously in a single call, rather than iterating and inserting each one separately.
In Jazz, we use three main cryptographic algorithms: XSalsa20 for encrypting data, BLAKE3, for calculating a rolling hash of appended operations, and Ed25519 for signing the hashes. These are all published and available for anyone to use.
When uploading an app to the Apple App Store, users have to make declarations about whether their app uses standard encryption or not as part of legal requirements around ‘exporting’ cryptography in the US. This section is intended to provide information for Jazz’s users to make informed decisions when responding to these questions.
I identified a bug where TypeScript incorrectly required specific options to load a CoFeed. However, the underlying code functionality did not depend on these options. I modified the system to remove this unnecessary requirement, improving its usability.
I wrote a new section for our docs explaining how to test Jazz apps using a variety of different helpers.
A number of the packages we depend on have become outdated and have security vulnerabilities. I took care of updating these packages to their latest versions and fixed the issues caused by API changes (particularly for Better Auth).
Our previous ImageDefinition docs were spread over four files, representing a nightmare for maintainability. I consolidated them into a single file, merging all the content and code samples together to make it easier to avoid mistakes and reduce repetition.
Our previous ‘not found’ page was not particularly useful or professional. The new ‘not found’ page includes a search widget to allow users to find whatever content it was they were looking for when they landed on the ‘not found’ page.
Currently, the create-jazz-app CLI tool fetches an llms-full.txt file which contains a summary of all the content contained in the docs, regardless of framework.
However, since my previous PR got merged, we’re generating framework-specific llms-full.txt files, which will be a) smaller and b) less confusing for LLMs, so now, only the appropriate llms-full.txt gets pulled.
Currently, the create-jazz-app CLI tool fetches an llms-full.txt file which contains a summary of all the content contained in the docs, regardless of framework.
However, since my previous PR got merged, we’re generating framework-specific llms-full.txt files, which will be a) smaller and b) less confusing for LLMs, so now, only the appropriate llms-full.txt gets pulled.
I continued developing our llms.txt files. There are three main components to this PR:\
I created a new section in our docs which covers advanced performance tips for the 20% of users who need to push harder on performance than the majority.
Our CLI tool allows you to get started quickly building with Jazz by running npx create-jazz-app. This update gives some more starter options to users running the CLI tool.
So far, our users have been able to create invite links which target specific CoValues. But there’s no way to easily get the inviteSecret_ for processing however the user likes.
I created a couple of methods (Group.createInvite(groupId, role) and group.$jazz.createInvite(role) which allow you to manually get the secret, and process it how you like using Account.acceptInvite(). I also improved the API for acceptInvite so that if the invite is not specified, it’s assumed to be a group invitation.
Previously, our LLMs text file was done simply by searching and replacing naively.
I introduced a step where our docs are rendered out as pure markdown before they are exported. This allows us to mock specific components so we can do things like render the tabbed code group component sensibly.
There was a PR which introduced a bug where buttons all had additional absolutely positioned content after them, which resulted in every section containing a button to horizontally overflow its container, resulting in unsightly scrollbars popping up all over the website. I fixed it.
We have a tool which allows you to run create-jazz-app with some options to get a new Jazz app set up. This tool was cloning a specific part of our monorepo where a static build of our docs had been copied. However, this had fallen out of date, and so we were wasting our users’ time, bandwidth, and worse, seeding their AI agents with incorrect, outdated data.
I introduced functionality to fetch the latest docs from our website to save for AIs to refer to.
macOS defaults to using a weird overlay scroll bar when you’re using a trackpad, which meant that the scrollbars were rendering over content, which looked ugly and unprofessional. I fixed it.
We were starting to outgrow our existing nav structure for docs, with tens of articles and only two hierarchical levels. I extended the hierarchy to allow a third level of nesting, as well as collapsible subheadings in the nav menu.
Jazz allows you to authenticate in many different ways. One way is using passkeys, and this was covered in the authentication quickstart guide I published last week. This adds an additional authentication method, passphrases, and shows how to use the two different authentication methods in conjunction to create an effective ‘recovery key’ option.
We have some automated tests that run when we add new code to our repositories. For a while now, the end-to-end tests have been failing. I spent some time digging into the reasons for this, and fixed the tests so that they no longer fail when newer versions of Node are used to run the tests.
Our existing SSR agent was recommended for use to allow Server-Side Rendering in Next.js apps. However, I realised that the module itself is written in pure TypeScript, and there’s no good reason for it to be React-only. I modified the Svelte provider, changed the export, and updated the docs, as well as creating a small example app demonstrating how the SSR agent can be used to perform Server-Side Rendering in SvelteKit too.
Most of our users need basic features like authentication and collaboration in their apps, but the guides we have are currently very reference-y and not so much practical implementation guides.
I wrote one guide that covers permissions in Jazz using groups, and how to share data and collaborate on it, and a second which shows how to add passkey authentication into your app using the basic passkey UI.
This should make it easier for users to build an app with the kind of features a real-world app needs.
I created a GetAPIKey snippet which can be re-used all over the docs to have a single, consistent message regarding API keys, and allow us to simplify updating and maintaining all these references.
The snippet can also optionally display how the API key can be used.
We have a CodeGroup component which allows us to display some code with a copy button. This CodeGroup allows us to write special comments like // [!code --:1] to mark a line as ‘removed’. However, these were still being copied, resulting in inaccurate code snippets being copied when users clicked the button.
This PR removes those lines from the copied text.
In principle, according to the WebAuthn spec, cryptographic challenges should be at least 16 bytes long, however in our existing implementation, we had 3 bytes. Although this is technically within spec, it was causing issues for some users using third party passkey managers such as KeePassXC.
This PR builds on previous work and extends the challenge length to 20 bytes.
Due to various challenges with TypeScript generics, we can’t make the $jazz.ensureLoaded, $jazz.set and $jazz.subscribe methods typesafe on discriminated unions. This was causing warnings for one of our adopters in their project.
Although this was previously covered in the docs, it was hidden towards the bottom of the page.
This PR makes it clearer and more prominent.
By default, all credentials are stored under jazz but this makes it impossible to avoid namespace clashes (for example, if you have multiple apps running on the same domain, or in case you want to do local development of more than one Jazz app at a time.
This PR extends on work that was already done to add an option to the Svelte provider, allowing users to specify the prefix under which their credentials will be stored.
When users try to build their applications, TypeScript will compile Jazz as part of their dependencies. However, there are a couple of options that Jazz doesn’t currently work well with. This PR makes it clear to users what options need to be set for compilation to work properly.
Our documentation on how to subscribe to CoValues and load them deeply was complex and difficult to reason about. I simplified, consolidated the examples, and created new illustrative content to make it clearer and easier to understand for users of varying knowledge levels.
We launched our cloud dashboard recently, allowing users to sign up for API keys rather than using their email address. However, across all of our docs, we had verbiage that users should use their email as a temporary API key.
This PR addresses that by changing the wording to drive traffic to our dashboard instead of recommending email addresses.
We have a component in React, React Native and Svelte which can be used to display images with progressive sizes. Although we have an option to show a placeholder, and loading this is normally fast, in case there is a slower load (e.g. network issue, etc.), then there is a flash where the browser’s default placeholder will be displayed (normally just the alt text of the image).
This PR fixes that by instead setting the image to a transparent single pixel (which gets stretched as needed to fit the image based on the size needed by the user). Instead of getting a flash of alt text, nothing will display instead, until the Image Definition is loaded, at which point the default behaviour (show the placeholder or the alt text) will kick in.
Previously, users who wanted to get started with Jazz could either use the command line tool to scaffold a project, or they could work through an installation tutorial which covers the practical side of getting Jazz added to an app, but doesn’t guide folks to an ‘ah ha!’ moment.
I wrote a quickstart guide which works for both React and Svelte, and added a follow up which shows how to use Jazz on the server with a worker. These guides will help users understand the core concepts of Jazz.
Our existing docs explained how to use getters to recursively reference schemas, but weren’t very clear about how to avoid ReferenceErrors caused by schemas creating circular references which resulted in references being evaluated while they were still in the temporal dead zone.
I added some additional keywords and explanation to help folks find these docs more easily when they’re searching, as well as explaining the use cases in a bit more detail.
Web pages often have ‘links’ that can change part of the page without reloading the whole thing. These changes sometimes show different content depending on the link. In Jazz, we can invite people to collaborate on data using links, and we need to notice when someone follows a link.
Previously, the code that checks the URL for invitations only checked once — when the component first appeared on the page. That worked if the person opened the page fresh, but it wouldn’t notice if the URL changed later (for example, if someone clicked a link that added extra info to the URL, called a ‘hash’).
What I did was adjust the code so it also watches for changes to that part of the URL — the hash — so it reacts immediately whenever someone follows a link, not just on page load. That way, no invitation is missed, no matter how the user navigates.