Atomic Query
A client-side server-state cache with the API shape you already know from React Query — useQuery, useMutation, useInfiniteQuery, useSuspenseQuery — but built on top of @mongez/react-atom. One reactive system instead of two: server state and ephemeral UI state both live as atoms, share devtools, and read with the same hooks.
Highlighted features
One unified cache atom
Every query lives in a single queryAtom. One mental model for server state and UI state, one devtools timeline, one source of truth.
queryAtom hooks
queryAtom.useQuery, useMutation, useInfiniteQuery, useSuspenseQuery — all the React Query shapes you expect.
Built-in list helpers
push, unshift, remove, replace, sort, reverse on cached lists — write optimistic updates without re-implementing the patterns.
<HydrateQueries> for SSR
Seed initial data from a framework loader (Next.js / Remix / TanStack Start) so the client’s first paint matches the server HTML — no flash, no hydration mismatch.
Smaller surface than TanStack
If you’re already on @mongez/atom, you don’t pay for a second reactive system. Lean API, atom devtools for free.
Install
npm install @mongez/atomic-queryyarn add @mongez/atomic-querypnpm add @mongez/atomic-queryPeer: react >= 18. Runtime deps: @mongez/events, @mongez/react-atom (installed automatically).
Quick peek
"use client";import { queryAtom } from "@mongez/atomic-query";
export function UserList() { const { data, isLoading, error } = queryAtom.useQuery<User[]>({ queryKey: ["users"], queryFn: ({ signal }) => fetch("/api/users", { signal }).then(r => r.json()), staleTime: 60_000, });
if (isLoading) return <Spinner />; if (error) return <ErrorBox error={error} />; return <ul>{data?.map(u => <li key={u.id}>{u.name}</li>)}</ul>;}A typed query with abort-on-stale, 60-second cache, and loading / error / data branches built in.
How it fits
atomic-query is the client-side cache, not a replacement for your framework’s data loader. The intended split:
- Framework loader (Next.js server component, Remix
loader, TanStack Startloader) — initial server render, produces the first dataset. - atomic-query — client takes over for mutations, optimistic updates, background refetches, invalidations, list manipulation.
SSR seeding
// server componentimport { HydrateQueries } from "@mongez/atomic-query";import { UserListClient } from "./UserListClient";
export default async function UsersPage() { const users = await db.users.findMany(); return ( <HydrateQueries entries={[{ queryKey: ["users"], data: users }]}> <UserListClient /> </HydrateQueries> );}Seeded data lands in the cache synchronously during render.
Client-only constraint
Every file carries "use client" and the exports map declares "react-server": null. React Server Components cannot import this package — the bundler will refuse to compile with a clear error. SSR is your framework’s job; this is the client cache.
When to use atomic-query vs TanStack Query
| Use atomic-query | Use TanStack Query |
|---|---|
Already using @mongez/atom | Not on @mongez/atom |
Want built-in list helpers (push, unshift, remove, …) | Need bidirectional infinite scroll (atomic-query is forward-only) |
| Smaller surface, comfortable owning SSR via framework loader | Need full TanStack feature set (Suspense, dehydrate/hydrate, normalisation) |
Where to go next
- Basic query, Queries, Mutations — core hooks
- Infinite queries, Suspense mode — advanced patterns
- List queries, List helpers — optimistic list mutations
- Invalidation, Cache management — cache control
- SSR —
<HydrateQueries>, framework loaders - Recipes — cross-cutting patterns