React Installation and Setup
Add Jazz to your React application in minutes. This setup covers standard React apps, Next.js, and gives an overview of experimental SSR approaches.
Integrating Jazz with React is straightforward. You'll define data schemas that describe your application's structure, then wrap your app with a provider that handles sync and storage. The whole process takes just three steps:
Looking for complete examples? Check out our example applications for chat apps, collaborative editors, and more.
Install dependencies
First, install the required packages:
pnpm install jazz-react jazz-tools
Write your schema
Define your data schema using CoValues from jazz-tools
.
// schema.ts import {
import co
co,import z
z } from "jazz-tools"; export constTodoItem =
const TodoItem: CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>
import co
co.map({
map<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>(shape: { title: z.z.ZodString; completed: z.z.ZodBoolean; }): CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }> export map
title: z.z.ZodString
title:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString export string
completed: z.z.ZodBoolean
completed:import z
z.boolean(), }); export const
function boolean(params?: string | z.z.core.$ZodBooleanParams): z.z.ZodBoolean export boolean
AccountRoot =
const AccountRoot: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }>
import co
co.map({
map<{ todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }>(shape: { todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }): CoMapSchema<...> export map
todos:
todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>
import co
co.list(
list<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>(element: CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>): CoListSchema<...> export list
TodoItem), }); export const
const TodoItem: CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>
MyAppAccount =
const MyAppAccount: AccountSchema<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }>; profile: CoMapSchema<{ name: z.z.ZodString; }>; }>
import co
co.account({
account<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }>; profile: CoMapSchema<{ name: z.z.ZodString; }>; }>(shape?: { root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }>; profile: CoMapSchema<{ name: z.z.ZodString; }>; } | undefined): AccountSchema<...> export account
root:
root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }>
AccountRoot,
const AccountRoot: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: z.z.ZodString; completed: z.z.ZodBoolean; }>>; }>
profile:
profile: CoMapSchema<{ name: z.z.ZodString; }>
import co
co.map({
map<{ name: z.z.ZodString; }>(shape: { name: z.z.ZodString; }): CoMapSchema<{ name: z.z.ZodString; }> export map
name: z.z.ZodString
name:import z
z.string() }), });
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString export string
See CoValues for more information on how to define your schema.
Standard React Setup
Wrap your application with <JazzProvider />
to connect to the Jazz network and define your data schema:
// app.tsx import {
function JazzProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, experimental_enableSSR, }: JazzProviderProps<S>): JSX.Element
JazzProvider } from "jazz-react"; import {MyAppAccount } from "./schema";
const MyAppAccount: AccountSchema<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: ZodString; completed: ZodBoolean; }>>; }>; profile: CoMapSchema<...>; }>
function createRoot(container: Container, options?: RootOptions): Root
createRoot lets you create a root to display React components inside a browser DOM node.createRoot(var document: Document
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementById(elementId: string): HTMLElement | null
Returns a reference to the first object with the specified value of the ID attribute.getElementById("root")!).Root.render(children: React.ReactNode): void
render( <function JazzProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, experimental_enableSSR, }: JazzProviderProps<S>): JSX.Element
JazzProvidersync: SyncConfig
sync={{peer: "wss://cloud.jazz.tools/?key=you@example.com"
peer: "wss://cloud.jazz.tools/?key=you@example.com" }}AccountSchema={
AccountSchema?: AccountSchema<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: ZodString; completed: ZodBoolean; }>>; }>; profile: CoMapSchema<...>; }> | undefined
MyAppAccount} > <
const MyAppAccount: AccountSchema<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: ZodString; completed: ZodBoolean; }>>; }>; profile: CoMapSchema<...>; }>
function App(): React.JSX.Element
App /> </function JazzProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, experimental_enableSSR, }: JazzProviderProps<S>): JSX.Element
JazzProvider> );
This setup handles:
- Connection to the Jazz sync server
- Schema registration for type-safe data handling
- Local storage configuration
With this in place, you're ready to start using Jazz hooks in your components. Learn how to access and update your data.
Next.js Integration
Client-side Only (Easiest)
The simplest approach for Next.js is client-side only integration:
// @filename: schema.ts import { co, z } from "jazz-tools"; export const TodoItem = co.map({ title: z.string(), completed: z.boolean(), }); export const AccountRoot = co.map({ todos: co.list(TodoItem), }); export const MyAppAccount = co.account({ root: AccountRoot, profile: co.map({ name: z.string() }), }); // @filename: app.tsx import * as React from "react"; // ---cut--- // app.tsx "use client" // Mark as client component import { JazzProvider } from "jazz-react"; import { MyAppAccount } from "./schema"; export function JazzWrapper({ children }: { children: React.ReactNode }) { return ( <JazzProvider sync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com" }} AccountSchema={MyAppAccount} > {children} </JazzProvider> ); }
Remember to mark any component that uses Jazz hooks with "use client"
:
// Profile.tsx "use client"; import {
useAccount } from "jazz-react"; import {
function useAccount<A extends AccountClass<Account> | AnyAccountSchema>(AccountSchema?: A): { me: Loaded<A, true>; logOut: () => void; } (+1 overload)
MyAppAccount } from "./schema"; export function
const MyAppAccount: AccountSchema<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: ZodString; completed: ZodBoolean; }>>; }>; profile: CoMapSchema<...>; }>
function Profile(): React.JSX.Element
Profile() { const {me } =
const me: ({ profile: { name: string; } & CoMap & Profile; } & { root: ({ todos: CoList<({ title: string; completed: boolean; } & CoMap) | null> | null; } & CoMap) | null; profile: ({ name: string; } & CoMap) | null; } & Account) | null | undefined
useAccount(
useAccount<AccountSchema<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: ZodString; completed: ZodBoolean; }>>; }>; profile: CoMapSchema<...>; }>, { ...; }>(AccountSchema: AccountSchema<...>, options?: { ...; } | undefined): { ...; } (+1 overload)
MyAppAccount, {
const MyAppAccount: AccountSchema<{ root: CoMapSchema<{ todos: CoListSchema<CoMapSchema<{ title: ZodString; completed: ZodBoolean; }>>; }>; profile: CoMapSchema<...>; }>
resolve: {
resolve?: RefsToResolve<{ root: ({ todos: CoList<({ title: string; completed: boolean; } & CoMap) | null> | null; } & CoMap) | null; profile: ({ name: string; } & CoMap) | null; } & Account, 10, []> | undefined
profile: true } }); return <
profile?: RefsToResolve<{ name: string; } & CoMap & Profile, 10, [0]> | undefined
JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>Hello, {me?.
const me: ({ profile: { name: string; } & CoMap & Profile; } & { root: ({ todos: CoList<({ title: string; completed: boolean; } & CoMap) | null> | null; } & CoMap) | null; profile: ({ name: string; } & CoMap) | null; } & Account) | null | undefined
profile.
Account.profile: { name: string; } & CoMap & Profile
Profile.name: string | undefined
name}</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>; }
SSR Support (Experimental)
For server-side rendering, Jazz offers experimental approaches:
- Pure SSR
- Hybrid SSR + Client Hydration
Pure SSR
Use Jazz in server components by directly loading data with CoValue.load()
.
This works well for public data accessible to the server account.
Hybrid SSR + Client Hydration
For more complex cases, you can pre-render on the server and hydrate on the client:
- Create a shared rendering component.
- Create a client hydration component.
- Create a server component that pre-loads data.
This approach gives you the best of both worlds: fast initial loading with server rendering, plus real-time updates on the client.
Further Reading
- Schemas - Learn about defining your data model
- Provider Configuration - Learn about other configuration options for Providers
- Authentication - Set up user authentication
- Sync and Storage - Learn about data persistence