Skip to content

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

Terminal window
npm install @mongez/atomic-query

Peer: 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 Start loader) — initial server render, produces the first dataset.
  • atomic-query — client takes over for mutations, optimistic updates, background refetches, invalidations, list manipulation.

SSR seeding

// server component
import { 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-queryUse TanStack Query
Already using @mongez/atomNot 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 loaderNeed full TanStack feature set (Suspense, dehydrate/hydrate, normalisation)

Where to go next