React

Zero has built-in support for React. Here’s what basic usage looks like:

import {useQuery, useZero} from '@rocicorp/zero/react';
import {type Schema} from './schema.ts';
import {type Mutators} from './mutators.ts';

function IssueList() {
  const z = useZero<Schema, Mutators>();
  let issueQuery = z.query.issue
    .related('creator')
    .related('labels')
    .limit(100);

  const userID = selectedUserID();

  if (userID) {
    issueQuery = issueQuery.where('creatorID', '=', userID);
  }

  const [issues, issuesDetail] = useQuery(issueQuery);

  return (
    <>
      <div>
        {issuesDetail.type === 'complete' ? 'Complete' : 'Partial'}
        results
      </div>
      <div>
        {issues.map(issue => (
          <IssueRow issue={issue} />
        ))}
      </div>
    </>
  );
}

ZeroProvider

The useZero hook must be used within a ZeroProvider component. The ZeroProvider component is responsible for creating and destroying Zero instances reactively.

import {SessionProvider} from 'my-auth-provider';
import {ZeroProvider} from '@rocicorp/zero/react';
import {type Schema, schema} from './schema.ts';
import {type Mutators, createMutators} from './mutators.ts';

export function Root() {
  const session = useSession();
  const {userID, authToken: auth} = session;
  const server = import.meta.env.VITE_PUBLIC_SERVER;

  const mutators = useMemo(() => {
    return createMutators(auth);
  }, [auth]);

  return (
    // ZeroProvider will reactively create and destroy Zero instances
    // as needed when props change.
    <ZeroProvider {...{userID, auth, schema, mutators, server}}>
      <App />
    </ZeroProvider>
  );
}

You can also pass a Zero instance to the ZeroProvider if you want to control the lifecycle of the Zero instance yourself:

// ZeroProvider just sets up the context, it doesn't manage
// the lifecycle of the Zero instance.
<ZeroProvider zero={zero}>
  <App />
</ZeroProvider>

Complete quickstart here:

https://github.com/rocicorp/hello-zero