npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reckona/mreact-query

v0.0.194

Published

Server state and async cache primitives for mreact.

Readme

@reckona/mreact-query

@reckona/mreact-query provides server state and async cache primitives for mreact. Loaders can prefetch data on the server, while client components hydrate and continue using the same query cache.

Basic Usage

import {
  createQuery,
  createQueryClient,
  dehydrate,
  getQueryClient,
  hydrate,
  syncQueryClientAcrossTabs,
} from "@reckona/mreact-query";

const queryClient = createQueryClient();

await queryClient.prefetchQuery({
  queryKey: ["profile"],
  retry: 2,
  retryDelay: 100,
  queryFn: ({ signal }) => fetch("/api/profile", { signal }).then((res) => res.json()),
});

const state = dehydrate(queryClient);
hydrate(getQueryClient(), state);

Core APIs

  • createQueryClient() creates a query cache.
  • fetchQuery() and prefetchQuery() execute async data functions and cache their results.
  • Query functions receive { queryKey, signal }; pass signal to fetch() so cancelQueries() can abort in-flight work.
  • retry and retryDelay opt into bounded retries for transient failures.
  • Query results expose errorReason as "aborted", "retry-exhausted", "network", or "unknown" so UI can avoid treating cancellations like user-visible failures.
  • cancelQueries() aborts in-flight queries by key prefix without retrying the canceled request.
  • removeQueries() aborts matching in-flight queries, evicts matching cache entries, and resets subscribed observers to an empty pending result.
  • createQuery() creates a reactive query observer. It auto-fetches empty queries in browsers by default and remains observe-only during server render. Hydrated entries render immediately, then revalidate on mount unless their server updatedAt timestamp is still covered by staleTime; pass autoFetch: false to require loader-prefetched data only.
  • createQuery() accepts gcTime to evict an idle cache entry after the last observer disposes. It is disabled by default; pass a non-negative millisecond value when short-lived browser views should release data after unmount.
  • createQuery() can opt into browser revalidation with refetchOnWindowFocus, refetchOnReconnect, and refetchOnInvalidate. These hooks are disabled by default and refetch through the same cache entry and abort signal path as manual refetch().
  • createQuery() and createInfiniteQuery() accept refetchInterval in milliseconds for browser polling. Each interval is scheduled after the preceding fetch settles, pauses while the document is hidden, and is cleared automatically when the observer disposes.
  • createInfiniteQuery() stores cursor pages under one query key, exposes pages, pageParams, hasNextPage, and fetchNextPage(), and dedupes concurrent requests for the same next page.
  • createMutation() handles mutations and invalidation.
  • queryClient.setQueryData(queryKey, updater) can derive a new cached value from the previous value without fetching.
  • Mutation lifecycle hooks run in this order: onMutate, mutationFn, state update, onSuccess, query invalidation, then onSettled. On failure, state updates before onError and onSettled. The value returned by onMutate is passed to onError and onSettled, which supports optimistic rollback without external bookkeeping.
  • dehydrate() and hydrate() move query state from server to client while preserving each successful query's updatedAt timestamp for staleTime checks.
  • getQueryClient() returns the browser singleton query client.
  • syncQueryClientAcrossTabs() optionally coordinates same-origin browser tabs with BroadcastChannel and Web Locks. Query messages require a non-default scoped channel and an includeQuery allowlist; successful query data is shared only when broadcastQueryData or singleFlight is explicitly enabled within that scope.

Router Usage

Use the request-scoped query client inside loader, then hydrate the browser singleton returned by getQueryClient(). This keeps large apps centered around query keys instead of passing every server-state value through page props. Server integrations that call runWithQueryClient() directly must first install AsyncLocalStorage with installQueryAsyncStorage(); without a request scope storage, runWithQueryClient() throws instead of using module-level state.

Infinite Queries

Use createInfiniteQuery() for cursor timelines and feeds that should not hand-roll request dedupe or page state in components.

const feed = createInfiniteQuery(queryClient, {
  queryKey: ["timeline"],
  initialPageParam: null as string | null,
  queryFn: ({ pageParam, signal }) =>
    fetch(`/api/timeline?cursor=${pageParam ?? ""}`, { signal }).then((res) => res.json()),
  getNextPageParam: (lastPage) => lastPage.nextCursor,
});

await feed.fetchNextPage();
feed.result.get().pages.flatMap((page) => page.items);

Set refetchOnWindowFocus or refetchOnReconnect when a browser observer should refresh on visibility/focus or network reconnect. Dispose observers when a component unmounts so browser listeners are removed.

Cross-Tab Sync

Use syncQueryClientAcrossTabs() when multiple same-origin tabs should observe each other's invalidations or avoid duplicate focus/reconnect refetches. The function mutates the provided client and returns a disposer that restores the original methods.

const queryClient = getQueryClient();

const disposeQuerySync = syncQueryClientAcrossTabs(queryClient, {
  channel: `mreact-query:v1:user:${sessionId}`,
  includeQuery: (queryKey) => queryKey[0] === "dashboard",
  singleFlight: true,
});

The adapter only sends or receives query messages when channel is a non-default name scoped to the current authenticated user, tenant, or other namespace and includeQuery explicitly allows the query key. Channel names are not secrets or authorization boundaries: any trusted or untrusted script running in the same origin can open the same BroadcastChannel, observe shared successful data, or send messages for allowed keys. Use data sharing only when the whole same-origin runtime is trusted, and do not share tokens, authorization-bearing data, sensitive PII, or query keys that reveal sensitive information. invalidateQueries({ queryKey }) and removeQueries({ queryKey }) are broadcast inside that namespace by default; keyless invalidations and removals stay local. Set broadcastQueryData: true only for query keys whose successful data is safe to share inside the channel. singleFlight: true uses Web Locks when available and hands the leader's successful fetch result to follower tabs so only one tab needs to perform a same-key fetch; without Web Locks or the required scope options, singleFlight falls back to local fetch behavior without leader election or implicit result handoff.