@confect/react provides drop-in replacements for Convexās React hooks. Each hook automatically encodes your args and decodes return values through the Effect Schemas defined in your function specs. You work with the Schema Type (decoded) values on both sidesāthe hooks handle the round-trip to Convexās Encoded representation transparently.
Functions are referenced via refs (from confect/_generated/refs) instead of Convexās api object. Each ref carries the args, returns, and (optionally) error schemas from the corresponding function spec, which is what enables the automatic encoding and decoding.
Setup
The React provider setup is the same as vanilla Convex.src/main.tsx
useQuery
Encodes args using the specās args schema, passes them to Convex, and decodes the result using the specās returns schema. Returns a QueryResult<A, E>āa tagged union with Loading, Success, and Failure variants.
Given this spec:
confect/notes.spec.ts
{} (the Type of Schema.Struct({})) as args and returns a QueryResult<readonly notes.Doc["Type"][]>. Match it with QueryResult.match:
QueryResult also exposes the lower-level predicates QueryResult.isLoading, QueryResult.isSuccess, and QueryResult.isFailure for cases where pattern matching is awkward.
Typed errors
When the refās spec declares anerror schema, useQuery returns QueryResult<A, E> and QueryResult.match requires an onFailure handler that receives the decoded typed error. See Error Handling for how to declare error schemas.
error schema are not surfaced as Failure. They propagate the same way they do with convex/reactās useQuery (typically reaching the nearest error boundary).
Skipping queries
Pass"skip" instead of args to disable the query subscription. The hook returns a Loading variant whose skipped flag is true, which lets you distinguish a query that is genuinely in flight from one sitting idle because no args have been provided.
usePaginatedQuery
Loads data reactively from a paginated query, mirroring the ergonomics of usePaginatedQuery from convex/react. The ref must come from a spec defined with FunctionSpec.publicPaginatedQueryāpassing any other ref fails at runtime with an error pointing at the constructor.
Args are encoded using the specās user-args schema (paginationOpts is managed by the hook, not the caller), and each loaded page is decoded using the specās item schema. Pass "skip" instead of args to disable the query, like useQuery.
Returns a PaginatedQueryResult<Item, E>. The loaded variantsāLoadingFirstPage, LoadingMore, CanLoadMore, and Exhaustedāall carry results and isLoading, and CanLoadMore additionally carries loadMore, so the common UI needs only field access and a couple of predicates:
loadMore lives only on CanLoadMore, the one state it can make progress fromāthe underlying Convex hook exposes it on every status, but calling it while a page is in flight, once the list is exhausted, or after a failure is an intentional no-op there. Narrowing with isCanLoadMore (or matchās onCanLoadMore) makes that statically apparent instead of silently dropping the call.
PaginatedQueryResult also provides match for exhaustive pattern matching, and the predicates isLoadingFirstPage, isLoadingMore, isExhausted, isLoading, and isFailure.
Typed errors
When the spec declares anerror schema, the result type gains a Failure variant carrying the decoded typed errorāerrors are values, consistent with useQuery. Narrow with PaginatedQueryResult.isFailure (or handle onFailure in match) to reach it:
results, Failure included: when a later page fails, the pages already loaded are not discarded.
Without an error schema, the Failure variant is excluded from the type entirely, so no narrowing is needed. Failures not declared in the error schema are thrown during render and propagate to the nearest error boundary.
usePaginatedQuery requires convex 1.36.0 or newer.useStreamPaginatedQuery
Loads data reactively from a paginated query whose handler paginates a query stream with QueryStream.paginate. Those pages are not tracked by Convexās query journal, which usePaginatedQuery relies on to keep pages consistent as data changes, so this hook maintains that guarantee itself: every loaded page is pinned to a fixed index range by echoing its continueCursor back as the next subscriptionās endCursor. Adjacent pages always meet exactly, a document is never shown twice or lost between pages, and a page that outgrows initialNumItems is split in two.
The call shape and the result are the same as usePaginatedQuery: args are encoded with the specās user-args schema, pages are decoded with its item schema, "skip" disables the query, and the result is a PaginatedQueryResult<Item, E> with a Failure variant when the spec declares an error schema.
maximumRowsRead and maximumBytesRead, per-page read budgets forwarded to the server. On a stream that filters out most of what it reads, a page that would otherwise scan past Convexās query limits returns truncated with SplitRequired instead, and the hook splits it.
When the server fails a page with paginationError: "InvalidCursor", the hook restarts pagination from the first page rather than failing. Stream queries emit this signal for malformed cursors, unsupported cursor versions, or mismatched runtime key-field layoutsānot every change to query semantics. See Cursor format and validation.
useStreamPaginatedQuery also works with paginated queries that use the
built-in paginate, since it only relies on the endCursor protocol field.
It subscribes to one query per loaded page.useMutation
Returns a function that encodes args using the specās args schema, calls the Convex mutation, and decodes the result using the specās returns schema. The returned promiseās shape depends on whether the spec declares an error schema.
Without an error schema
The function returns Promise<A>, matching convex/reactās useMutation. Undeclared failures still reject the promise.
confect/notes.spec.ts
With an error schema
When the spec declares an error schema, the function returns Promise<Result<A, E>>. Unwrap with Result.match (or another Result combinator) to handle both branches. Undeclared failures still reject the promise.
confect/notes.spec.ts
useAction
Same shape as useMutation: returns Promise<A> when the ref has no error schema, and Promise<Result<A, E>> when it does.
confect/random.spec.ts