Skip to main content
Zero provides React and SolidJS hooks for seamless integration with your UI framework.

React Hooks

Import from @rocicorp/zero-react:
import { 
  useQuery, 
  useSuspenseQuery,
  useConnectionState,
  useZero,
  ZeroProvider 
} from '@rocicorp/zero-react';

useQuery()

Subscribe to a query and receive live updates.
function MyComponent() {
  const [issues, details] = useQuery(
    q.issue.where('status', '=', 'open')
  );
  
  if (details.type === 'unknown') {
    return <div>Loading...</div>;
  }
  
  return <div>{issues.length} open issues</div>;
}
query
Query | QueryRequest | Falsy
required
The query to subscribe to. Can be null, undefined, or false to disable the query.
options
UseQueryOptions | boolean
Query options or a boolean for enabled:
interface UseQueryOptions {
  enabled?: boolean;  // Whether to execute the query
  ttl?: TTL;         // Time to live after unsubscribe
}
returns
QueryResult<TReturn> | MaybeQueryResult<TReturn>
A tuple of [data, details]:
  • data: Query results (undefined if query is falsy)
  • details: Metadata with type of 'unknown', 'complete', or 'error'
type QueryResult<T> = readonly [HumanReadable<T>, QueryResultDetails];
type MaybeQueryResult<T> = readonly [HumanReadable<T> | undefined, QueryResultDetails];

useSuspenseQuery()

Suspense-enabled query hook that suspends until data is available.
function MyComponent() {
  const [issues, details] = useSuspenseQuery(
    q.issue.where('status', '=', 'open'),
    { suspendUntil: 'complete' }
  );
  
  // No loading state needed - component suspends until data is ready
  return <div>{issues.length} open issues</div>;
}
query
Query | QueryRequest | Falsy
required
The query to subscribe to
options
UseSuspenseQueryOptions | boolean
Suspense query options:
interface UseSuspenseQueryOptions extends UseQueryOptions {
  suspendUntil?: 'partial' | 'complete';
}
  • 'partial': Suspend until partial results or complete (default)
  • 'complete': Suspend until query fully completes
returns
QueryResult<TReturn> | MaybeQueryResult<TReturn>
Same as useQuery(), but will suspend the component until data meets the suspendUntil condition.

useConnectionState()

Subscribe to the Zero instance’s connection state.
function ConnectionIndicator() {
  const state = useConnectionState();
  
  return (
    <div>
      Status: {state.name}
      {state.name === 'error' && <div>Error: {state.reason}</div>}
    </div>
  );
}
returns
ConnectionState
The current connection state, one of:
type ConnectionState = 
  | {name: 'connecting', reason?: string}
  | {name: 'connected'}
  | {name: 'disconnected', reason: string}
  | {name: 'needs-auth', reason: {...}}
  | {name: 'error', reason: string}
  | {name: 'closed', reason: string}

useZero()

Access the Zero instance from context.
function MyComponent() {
  const z = useZero();
  
  const handleCreate = async () => {
    await z.mutate.issue.create({
      id: nanoid(),
      title: 'New issue'
    });
  };
  
  return <button onClick={handleCreate}>Create Issue</button>;
}
returns
Zero<Schema, Mutators, Context>
The Zero instance provided by ZeroProvider

ZeroProvider

Provider component that creates and manages a Zero instance.
import { ZeroProvider } from '@rocicorp/zero-react';

function App() {
  return (
    <ZeroProvider
      schema={schema}
      userID="user-123"
      auth={authToken}
      cacheURL="https://myapp.zero.ms"
    >
      <MyComponent />
    </ZeroProvider>
  );
}
children
ReactNode
required
Child components that will have access to the Zero instance
zero
Zero
Pass an existing Zero instance instead of creating a new one:
const z = new Zero({...});
<ZeroProvider zero={z}>{children}</ZeroProvider>
init
(zero: Zero) => void
Callback invoked after Zero instance is created:
<ZeroProvider
  schema={schema}
  userID="user-123"
  init={(z) => {
    console.log('Zero initialized:', z.clientID);
  }}
>
All ZeroOptions are also accepted as props when not providing an external zero instance.

useZeroOnline() (deprecated)

Subscribe to the online status of the Zero instance.
function OnlineIndicator() {
  const isOnline = useZeroOnline();
  return <div>{isOnline ? '🟢 Online' : '🔴 Offline'}</div>;
}
Deprecated: Use useConnectionState() instead for more detailed connection state.
returns
boolean
true if Zero is online, false otherwise

SolidJS Hooks

Import from @rocicorp/zero-solid:
import { 
  useQuery,
  createQuery,
  useConnectionState,
  useZero,
  ZeroProvider 
} from '@rocicorp/zero-solid';

useQuery()

Solid hook that creates a reactive query signal.
import { useQuery } from '@rocicorp/zero-solid';

function MyComponent() {
  const [issues, details] = useQuery(() => 
    q.issue.where('status', '=', 'open')
  );
  
  return (
    <Show when={details().type === 'complete'} fallback={<div>Loading...</div>}>
      <div>{issues().length} open issues</div>
    </Show>
  );
}
query
() => Query | QueryRequest | Falsy
required
A function returning the query to subscribe to. Reactive to track dependencies.
options
UseQueryOptions | boolean
Same as React’s useQuery options:
interface UseQueryOptions {
  enabled?: boolean;
  ttl?: TTL;
}
returns
[Accessor<T>, Accessor<Details>]
A tuple of Solid signals:
  • data(): Access the query results
  • details(): Access query metadata

createQuery()

Creates a query outside of a component context.
const [issues, details] = createQuery(
  z,
  () => q.issue.where('status', '=', 'open'),
  { enabled: true }
);
zero
Zero
required
The Zero instance
query
() => Query | QueryRequest | Falsy
required
A function returning the query
options
CreateQueryOptions
Same as UseQueryOptions
returns
[Accessor<T>, Accessor<Details>]
Tuple of Solid signals for data and details

useConnectionState()

Solid hook for subscribing to connection state.
import { useConnectionState } from '@rocicorp/zero-solid';

function ConnectionIndicator() {
  const state = useConnectionState();
  
  return <div>Status: {state().name}</div>;
}
returns
Accessor<ConnectionState>
A Solid signal returning the current connection state

useZero()

Access the Zero instance from Solid context.
import { useZero } from '@rocicorp/zero-solid';

function MyComponent() {
  const z = useZero();
  
  const handleCreate = () => {
    z.mutate.issue.create({
      id: nanoid(),
      title: 'New issue'
    });
  };
  
  return <button onClick={handleCreate}>Create Issue</button>;
}
returns
Zero<Schema, Mutators, Context>
The Zero instance provided by ZeroProvider

ZeroProvider

Solid provider component for Zero.
import { ZeroProvider } from '@rocicorp/zero-solid';

function App() {
  return (
    <ZeroProvider
      schema={schema}
      userID="user-123"
      auth={authToken}
      cacheURL="https://myapp.zero.ms"
    >
      <MyComponent />
    </ZeroProvider>
  );
}
Accepts the same props as React’s ZeroProvider.

useZeroOnline() (deprecated)

Solid hook for subscribing to online status.
function OnlineIndicator() {
  const isOnline = useZeroOnline();
  return <div>{isOnline() ? '🟢' : '🔴'}</div>;
}
Deprecated: Use useConnectionState() instead.
returns
Accessor<boolean>
A Solid signal returning the online status

Common Patterns

Conditional Queries

Disable queries by passing falsy values:
// React
const [issue, details] = useQuery(
  issueId ? q.issue.where('id', '=', issueId).one() : null
);

// Solid
const [issue, details] = useQuery(() => 
  issueId() ? q.issue.where('id', '=', issueId()).one() : null
);

Query TTL

Control how long queries remain cached after unmounting:
const [data, details] = useQuery(query, {
  ttl: 60  // Keep cached for 60 seconds after unmount
});

Error Handling

Handle query errors with the details object:
const [data, details] = useQuery(query);

if (details.type === 'error') {
  return (
    <div>
      <p>Error: {details.error.message}</p>
      <button onClick={details.retry}>Retry</button>
    </div>
  );
}

Connection Management

Manually control connections from components:
function LoginButton() {
  const z = useZero();
  const state = useConnectionState();
  
  const handleLogin = async () => {
    const token = await getAuthToken();
    await z.connection.connect({auth: token});
  };
  
  if (state.name === 'needs-auth') {
    return <button onClick={handleLogin}>Login</button>;
  }
  
  return <div>Connected</div>;
}

Build docs developers (and LLMs) love