Cache
λ Query provides a rich API for directly manipulating the cache. You can perform optimistic mutations, hydrate data from SSR, invalidate stale entries, and inspect the current cache state.
Cache Mutations
Section titled “Cache Mutations”Use mutate() to optimistically update cached data without a network request. This is commonly used after a user action where you know the new value without needing to refetch:
// Set a specific value.Mutation with Custom Expiration
Section titled “Mutation with Custom Expiration”By default, mutated values expire immediately (0ms), meaning they’ll be revalidated on the next query. Provide an expiration to keep them fresh:
await query.mutate("/api/user", updatedUser, { expiration(item) { return 5000; // Fresh for 5 seconds. },});Mutation Based on Previous Value
Section titled “Mutation Based on Previous Value”Pass a function to compute the new value from the previous one:
// Append an item to a list.await query.mutate("/api/posts", function (previous) { return [...previous, newPost];});
// The function also receives the current expiration date.await query.mutate("/api/posts", function (previous, expiresAt) { console.log("Current expiration:", expiresAt); return [...previous, newPost];});Async Mutation
Section titled “Async Mutation”The mutation function can be async. This is the most efficient way to perform mutations because it correctly triggers transition events, enabling the React isMutating state:
await query.mutate("/api/posts", async function (previous) { const newPost = await createPost({ title: "Hello World" }); return [...previous, newPost];});The mutation lifecycle fires two events:
mutating— with the promise (allows UI to show loading state).mutated— with the resolved value (UI updates to the new data).
Hydrating the Cache
Section titled “Hydrating the Cache”Use hydrate() to populate the cache with data you already have, such as data from server-side rendering:
// Hydrate a single key.query.hydrate("/api/user", userData);
// Hydrate with a specific expiration.query.hydrate("/api/user", userData, { expiration(item) { return 30_000; // Fresh for 30 seconds. },});
// Hydrate multiple keys with the same value.query.hydrate(["/api/post/1", "/api/post/2"], defaultPost);hydrate() is synchronous and does not trigger a fetch. By default, hydrated values have an expiration of 0ms (immediately expired), meaning they’ll be revalidated on first use. Provide an expiration function to keep them fresh.
SSR Hydration Pattern
Section titled “SSR Hydration Pattern”A common pattern for SSR is to fetch data on the server, serialize it, and hydrate the client cache before React renders:
// Server: fetch data and embed in HTML.const user = await fetchUser();const html = ` <script>window.__DATA__ = ${JSON.stringify({ user })}</script> <div id="root">${renderToString(<App />)}</div>`;
// Client: hydrate before rendering.const query = createQuery();query.hydrate("/api/user", window.__DATA__.user, { expiration: () => 30_000,});
createRoot(document.getElementById("root")).render( <QueryProvider query={query}> <App /> </QueryProvider>);Invalidating the Cache
Section titled “Invalidating the Cache”Use forget() to remove items from the cache. This triggers a forgotten event and causes any active useQuery hooks to refetch (when clearOnForget is enabled):
// Forget a single key.await query.forget("/api/user");
// Forget multiple keys.await query.forget(["/api/user", "/api/posts"]);
// Forget all keys matching a pattern.await query.forget(/^\/api\/posts/);
// Forget everything.await query.forget();forget() returns a promise to ensure events have been emitted before it resolves. It removes items from the items cache only — in-flight resolvers continue running (use abort() to cancel those).
Regex Invalidation
Section titled “Regex Invalidation”Use a regular expression to invalidate groups of related keys:
// Forget all post-related cache entries.await query.forget(/^\/api\/posts/);
// Forget all user-related entries.await query.forget(/\/api\/user/);This is especially useful after a mutation that affects multiple cache entries — for example, creating a new post should invalidate the posts list and any paginated post queries.
Inspecting the Cache
Section titled “Inspecting the Cache”List all currently cached keys:
const itemKeys = query.keys("items"); // Resolved items.const resolverKeys = query.keys("resolvers"); // In-flight requests.Expiration
Section titled “Expiration”Check when a specific item expires:
const expiresAt = query.expiration("/api/user");
if (expiresAt && expiresAt < new Date()) { console.log("User data is stale");}Snapshots
Section titled “Snapshots”Read the current cached value without triggering a fetch:
const user = await query.snapshot("/api/user");
if (user) { console.log("Cached user:", user);}Returns undefined if the key is not in the items cache.
Raw Cache Access
Section titled “Raw Cache Access”For advanced use cases, access the underlying cache instances directly:
const { items, resolvers } = query.caches();
// items is a Cache<ItemsCacheItem>// resolvers is a Cache<ResolversCacheItem>