Learn some Jazz
Jazz is a new kind of database that's distributed across your frontend, containers, serverless functions and its own storage cloud.
It syncs structured data, files and LLM streams instantly, and looks like local reactive JSON state.
It also provides auth, orgs & teams, real-time multiplayer, edit histories, permissions, E2E encryption and offline-support out of the box.
Quickstart
Show me
Check out our tiny To Do list example to see what Jazz can do in a nutshell.
Help me understand
Follow our quickstart guide for a more detailed guide on building a simple app with Jazz.
Just want to get started?
You can use create-jazz-app to create a new Jazz project from one of our starter templates or example apps:
npx create-jazz-app@latest --api-key you@example.com
Using an LLM? Add our llms.txt to your context window!
Requires at least Node.js v20. See our Troubleshooting Guide for quick fixes.
How it works
- Define your data with CoValues schemas
- Connect to storage infrastructure (Jazz Cloud or self-hosted)
- Create and edit CoValues locally
- Get automatic sync and persistence across all devices and users
Your UI updates instantly on every change, everywhere. It's like having reactive local state that happens to be shared with the world.
A Minimal Jazz App
Here, we'll scratch the surface of what you can do with Jazz. We'll build a quick and easy To Do list app — easy to use, easy to build, and easy to make comparisons with!
This is the end result: we're showing it here running in two iframes, updating in real-time through the Jazz Cloud.
Try adding items on the left and watch them appear instantly on the right!
These two iframes are syncing through the Jazz Cloud. You can use the toggle in the top right to switch between 'online' and 'offline' on each client, and see how with Jazz, you can keep working even when you're offline.
Imports
Start by importing Jazz into your app.
import { co, z } from 'jazz-tools'; import { JazzBrowserContextManager } from 'jazz-tools/browser';
Schema
Then, define what your data looks like using Collaborative Values — the building blocks that make Jazz apps work.
const ToDo = co.map({ title: z.string(), completed: z.boolean() }); const ToDoList = co.list(ToDo);
Context
Next, give your app some context and tell Jazz your sync strategy — use the Jazz Cloud to get started quickly. We'll also create our to do list and get its ID here to use later.
await new JazzBrowserContextManager().createContext({ sync: { peer: 'wss://cloud.jazz.tools?key=minimal-vanilla-example', when: 'always', }, }); const newList = ToDoList.create([{ title: 'Learn Jazz', completed: false }]); const listId = newList.$jazz.id;
Build your UI
Now, build a basic UI skeleton for your app.
const app = document.querySelector('#app')!; const id = Object.assign(document.createElement('small'), { innerText: `List ID: ${listId}`, }); const listContainer = document.createElement('div'); app.append(listContainer, id);
Display Items
Display your items and add logic to mark them as done...
function toDoItemElement(todo: co.loaded<typeof ToDo>) { const label = document.createElement('label'); const checkbox = Object.assign(document.createElement('input'), { type: 'checkbox', checked: todo.completed, onclick: () => todo.$jazz.set('completed', checkbox.checked), }); label.append(checkbox, todo.title); return label; }
Add New Items
...and add new items to the list using an input and a button.
function newToDoFormElement(list: co.loaded<typeof ToDoList>) { const form = Object.assign(document.createElement('form'), { onsubmit: (e: Event) => { e.preventDefault(); list.$jazz.push({ title: input.value, completed: false }); } }); const input = Object.assign(document.createElement('input'), { placeholder: 'New task', }); const btn = Object.assign(document.createElement('button'), { innerText: 'Add', }); form.append(input, btn); return form; }
Subscribe to Changes
Now for the magic: listen to changes coming from anyone, anywhere, and update your UI in real time.
const unsubscribe = ToDoList.subscribe( listId, { resolve: { $each: true } }, (toDoList) => { const addForm = newToDoFormElement(toDoList); listContainer.replaceChildren( ...toDoList.map((todo) => { return toDoItemElement(todo); }), addForm ); } );
Simple Routing
Lastly, we'll add a tiny bit of routing logic to be able to share the list by URL: if there's an id search parameter, that'll be the list we'll subscribe to later. If we don't have an id, we'll create a new ToDo list. We'll replace the section where we created the ToDoList above.
const newList = ToDoList.create([{ title: 'Learn Jazz', completed: false }]); const listId = newList.$jazz.id; const listId = new URLSearchParams(window.location.search).get('id'); if (!listId) { const newList = ToDoList.create([{ title: 'Learn Jazz', completed: false }]); await newList.$jazz.waitForSync(); window.location.search = `?id=${newList.$jazz.id}`; throw new Error('Redirecting...'); }
All Together
Put it all together for a simple Jazz app in less than 100 lines of code.
import { co, z } from 'jazz-tools'; import { JazzBrowserContextManager } from 'jazz-tools/browser'; const ToDo = co.map({ title: z.string(), completed: z.boolean() }); const ToDoList = co.list(ToDo); await new JazzBrowserContextManager().createContext({ sync: { peer: 'wss://cloud.jazz.tools?key=minimal-vanilla-example', when: 'always', }, }); const listId = new URLSearchParams(window.location.search).get('id'); if (!listId) { const newList = ToDoList.create([{ title: 'Learn Jazz', completed: false }]); await newList.$jazz.waitForSync(); window.location.search = `?id=${newList.$jazz.id}`; throw new Error('Redirecting...'); } const app = document.querySelector('#app')!; const id = Object.assign(document.createElement('small'), { innerText: `List ID: ${listId}`, }); const listContainer = document.createElement('div'); app.append(listContainer, id); function toDoItemElement(todo: co.loaded<typeof ToDo>) { const label = document.createElement('label'); const checkbox = Object.assign(document.createElement('input'), { type: 'checkbox', checked: todo.completed, onclick: () => todo.$jazz.set('completed', checkbox.checked), }); label.append(checkbox, todo.title); return label; } function newToDoFormElement(list: co.loaded<typeof ToDoList>) { const form = Object.assign(document.createElement('form'), { onsubmit: (e: Event) => { e.preventDefault(); list.$jazz.push({ title: input.value, completed: false }); } }); const input = Object.assign(document.createElement('input'), { placeholder: 'New task', }); const btn = Object.assign(document.createElement('button'), { innerText: 'Add', }); form.append(input, btn); return form; } const unsubscribe = ToDoList.subscribe( listId, { resolve: { $each: true } }, (toDoList) => { const addForm = newToDoFormElement(toDoList); listContainer.replaceChildren( ...toDoList.map((todo) => { return toDoItemElement(todo); }), addForm ); } );
Want to see more?
Have a look at our example apps for inspiration and to see what's possible with Jazz. From real-time chat and collaborative editors to file sharing and social features — these are just the beginning of what you can build.
If you have any questions or need assistance, please don't hesitate to reach out to us on Discord. We'd love to help you get started.