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:

  1. Install dependencies
  2. Write your schema
  3. Wrap your app in <JazzReactProvider />

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-tools
Requires at least Node.js v20.

Write your schema

Define your data schema using CoValues from jazz-tools.

// schema.ts
import { import coco, import zz } from "jazz-tools";

export const 
const TodoItem: CoMapSchema<{
    title: z.z.ZodString;
    completed: z.z.ZodBoolean;
}>
TodoItem
= import coco.
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
map
({
title: z.z.ZodStringtitle: import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString
export string
string
(),
completed: z.z.ZodBooleancompleted: import zz.
function boolean(params?: string | z.z.core.$ZodBooleanParams): z.z.ZodBoolean
export boolean
boolean
(),
}); export const
const AccountRoot: CoMapSchema<{
    todos: CoListSchema<CoMapSchema<{
        title: z.z.ZodString;
        completed: z.z.ZodBoolean;
    }>>;
}>
AccountRoot
= import coco.
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
map
({
todos: CoListSchema<CoMapSchema<{
    title: z.z.ZodString;
    completed: z.z.ZodBoolean;
}>>
todos
: import coco.
list<CoMapSchema<{
    title: z.z.ZodString;
    completed: z.z.ZodBoolean;
}>>(element: CoMapSchema<{
    title: z.z.ZodString;
    completed: z.z.ZodBoolean;
}>): CoListSchema<...>
export list
list
(
const TodoItem: CoMapSchema<{
    title: z.z.ZodString;
    completed: z.z.ZodBoolean;
}>
TodoItem
),
}); export const
const MyAppAccount: AccountSchema<{
    root: CoMapSchema<{
        todos: CoListSchema<CoMapSchema<{
            title: z.z.ZodString;
            completed: z.z.ZodBoolean;
        }>>;
    }>;
    profile: CoMapSchema<{
        name: z.z.ZodString;
    }>;
}>
MyAppAccount
= import coco.
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
account
({
root: CoMapSchema<{
    todos: CoListSchema<CoMapSchema<{
        title: z.z.ZodString;
        completed: z.z.ZodBoolean;
    }>>;
}>
root
:
const AccountRoot: CoMapSchema<{
    todos: CoListSchema<CoMapSchema<{
        title: z.z.ZodString;
        completed: z.z.ZodBoolean;
    }>>;
}>
AccountRoot
,
profile: CoMapSchema<{
    name: z.z.ZodString;
}>
profile
: import coco.
map<{
    name: z.z.ZodString;
}>(shape: {
    name: z.z.ZodString;
}): CoMapSchema<{
    name: z.z.ZodString;
}>
export map
map
({ name: z.z.ZodStringname: import zz.
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString
export string
string
() }),
});

See CoValues for more information on how to define your schema.

Standard React Setup

Wrap your application with <JazzReactProvider /> to connect to the Jazz network and define your data schema:

// app.tsx
import { function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
} from "jazz-tools/react";
import {
const MyAppAccount: AccountSchema<{
    root: CoMapSchema<{
        todos: CoListSchema<CoMapSchema<{
            title: ZodString;
            completed: ZodBoolean;
        }>>;
    }>;
    profile: CoMapSchema<...>;
}>
MyAppAccount
} from "./schema";
function createRoot(container: Container, options?: RootOptions): Root
createRoot lets you create a root to display React components inside a browser DOM node.
@see{@link https://react.dev/reference/react-dom/client/createRoot API Reference for `createRoot`}
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.
@paramelementId String that specifies the ID value.
getElementById
("root")!).Root.render(children: React.ReactNode): voidrender(
<function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
sync: SyncConfigsync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com"peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
AccountSchema?: AccountSchema<{
    root: CoMapSchema<{
        todos: CoListSchema<CoMapSchema<{
            title: ZodString;
            completed: ZodBoolean;
        }>>;
    }>;
    profile: CoMapSchema<...>;
}> | undefined
AccountSchema
={
const MyAppAccount: AccountSchema<{
    root: CoMapSchema<{
        todos: CoListSchema<CoMapSchema<{
            title: ZodString;
            completed: ZodBoolean;
        }>>;
    }>;
    profile: CoMapSchema<...>;
}>
MyAppAccount
}
> <function App(): React.JSX.ElementApp /> </function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
>
);

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

Normally Jazz doesn't render the children until the account is loaded.

On the server there is no account, but we can ask Jazz to render the children using an "empty agent". An agent is a read-only account that can be used without credentials to render the data available to the public.

import { function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
} from "jazz-tools/react";
import {
const MyAppAccount: AccountSchema<{
    root: CoMapSchema<{
        todos: CoListSchema<CoMapSchema<{
            title: ZodString;
            completed: ZodBoolean;
        }>>;
    }>;
    profile: CoMapSchema<...>;
}>
MyAppAccount
} from "./schema";
export function
function JazzWrapper({ children }: {
    children: React.ReactNode;
}): React.JSX.Element
JazzWrapper
({ children: React.ReactNodechildren }: { children: React.ReactNodechildren: React.type React.ReactNode = string | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<...> | null | undefined
Represents all of the things React can render. Where {@link ReactElement } only represents JSX, `ReactNode` represents everything that can be rendered.
@see{@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/reactnode/ React TypeScript Cheatsheet}@example```tsx // Typing children type Props = { children: ReactNode } const Component = ({ children }: Props) => <div>{children}</div> <Component>hello</Component> ```@example```tsx // Typing a custom element type Props = { customElement: ReactNode } const Component = ({ customElement }: Props) => <div>{customElement}</div> <Component customElement={<div>hello</div>} /> ```
ReactNode
}) {
return ( <function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
enableSSR?: boolean | undefinedenableSSR // Renders the components tree in the server using an agent sync: SyncConfigsync={{ peer: "wss://cloud.jazz.tools/?key=you@example.com"peer: "wss://cloud.jazz.tools/?key=you@example.com" }}
AccountSchema?: AccountSchema<{
    root: CoMapSchema<{
        todos: CoListSchema<CoMapSchema<{
            title: ZodString;
            completed: ZodBoolean;
        }>>;
    }>;
    profile: CoMapSchema<...>;
}> | undefined
AccountSchema
={
const MyAppAccount: AccountSchema<{
    root: CoMapSchema<{
        todos: CoListSchema<CoMapSchema<{
            title: ZodString;
            completed: ZodBoolean;
        }>>;
    }>;
    profile: CoMapSchema<...>;
}>
MyAppAccount
}
> {children: React.ReactNodechildren} </function JazzReactProvider<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>({ children, guestMode, sync, storage, AccountSchema, defaultProfileName, onLogOut, logOutReplacement, onAnonymousAccountDiscarded, enableSSR, }: JazzProviderProps<S>): JSX.Element
@categoryContext & Hooks
JazzReactProvider
>
); }

Since the agent used is empty, all the useCoState and useAccount will return null.

If you want to render the data on the server you can use createSSRJazzAgent to generate a read-only account to load the data:

import { 
function createSSRJazzAgent(opts: {
    peer: string;
}): import("/vercel/path0/packages/jazz-tools/dist/index").AnonymousJazzAgent
createSSRJazzAgent
} from "jazz-tools/react/ssr";
import {
const TodoItem: CoMapSchema<{
    title: ZodString;
    completed: ZodBoolean;
}>
TodoItem
} from "./schema";
// This can be created in a centralized module, to reuse the same agent in all the server components export const const jazzSSR: AnonymousJazzAgentjazzSSR =
function createSSRJazzAgent(opts: {
    peer: string;
}): import("/vercel/path0/packages/jazz-tools/dist/index").AnonymousJazzAgent
createSSRJazzAgent
({
peer: stringpeer: "wss://cloud.jazz.tools/", }); export default async function
function ServerSidePage(props: {
    params: Promise<{
        itemId: string;
    }>;
}): Promise<React.JSX.Element>
ServerSidePage
(
props: {
    params: Promise<{
        itemId: string;
    }>;
}
props
: {
params: Promise<{
    itemId: string;
}>
params
: interface Promise<T>
Represents the completion of an asynchronous operation
Promise
<{ itemId: stringitemId: string }>;
}) { const { const itemId: stringitemId } = await
props: {
    params: Promise<{
        itemId: string;
    }>;
}
props
.
params: Promise<{
    itemId: string;
}>
params
;
const
const item: ({
    title: string;
    completed: boolean;
} & CoMap) | null
item
= await
const TodoItem: CoMapSchema<{
    title: ZodString;
    completed: ZodBoolean;
}>
TodoItem
.
load<true>(id: string, options?: {
    resolve?: RefsToResolve<{
        title: string;
        completed: boolean;
    } & CoMap, 10, []> | undefined;
    loadAs?: Account | AnonymousJazzAgent;
    skipRetry?: boolean;
} | undefined): Promise<...>
load
(const itemId: stringitemId, {
loadAs?: AnonymousJazzAgent | Account | undefinedloadAs: const jazzSSR: AnonymousJazzAgentjazzSSR, }); return ( <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="flex flex-col items-center justify-center h-screen gap-4"> <React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1 React.HTMLAttributes<T>.className?: string | undefinedclassName="text-2xl font-bold">SSR rendering example with Jazz</React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1> <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="text-sm text-gray-500 w-1/2 text-center"> This is a server component! </React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> <React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>label> <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="text-sm">Item title "{
const item: ({
    title: string;
    completed: boolean;
} & CoMap) | null
item
?.title: string | undefinedtitle}"</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>label> </React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> ); }

Take a look on our Next.js example to see a complete example of how to use SSR with Jazz.

Further Reading