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

@desolint/tanstack-query

v0.0.1

Published

Framework-agnostic TanStack React Query orchestration layer (query/mutation handlers, SSR prefetch, toast + cache-invalidation wiring) for Desolint frontend projects.

Readme

@desolint/tanstack-query

Framework-agnostic TanStack React Query orchestration for Desolint frontend projects — standardized query/mutation hooks, refetch-mode configuration, toast side-effects, cache invalidation, and SSR prefetching, with zero hard dependency on any specific HTTP client or toast library.

Requirements

  • React 18 or newer (peer dependency)
  • npm 7 or newer — npm 7+ installs peer dependencies automatically

Install

npm install @desolint/tanstack-query

That is all you need on npm 7+: @tanstack/react-query, react are listed as peer dependencies, so npm resolves and installs them for you.

@tanstack/react-query-devtools is an optional peer — install it only if you want the devtools panel.

Neither installs peer dependencies automatically, so name them explicitly:

yarn add @desolint/tanstack-query @tanstack/react-query react
# or
pnpm add @desolint/tanstack-query @tanstack/react-query react

Why these are peer dependencies, not regular ones

This is the classic case. Two copies of React break hooks outright — React tracks hook state in module-level internals, so a component rendered by one copy and a hook from another throws Invalid hook call. Likewise, @tanstack/react-query holds the query cache in its own module scope: a second copy means a second, empty cache, so your queries would refetch instead of resolving from data you already have.

Declaring them as peer dependencies means npm reuses the copy your application already has instead of nesting a second one under this package. You keep control of the version; this package just states the range it works with.

Quick start

1. Build a config object

The package never talks to an HTTP client or a toast library directly — you provide adapters in a config object. There is no global setup call: the config is passed to the provider and lives in React context, so the client and server graphs never share or race on state.

// e.g. src/reactQueryConfig.client.ts
import type {ReactQueryConfig} from '@desolint/tanstack-query';

import {getRequest, requestFunctions} from './your-http-client';
import {showToast} from './your-toast-library';

export const reactQueryConfig: ReactQueryConfig = {
  request: getRequest, // used for GET, by useQueryHandler
  requestFunctions, // {POST, PUT, PATCH, DELETE}, used by useMutationHandler
  onToast: ({type, message}) => showToast({type, message}),
};

For SSR prefetch, build a second config that adds getServerRequest — keep it in a server-only module so getServerRequest never reaches the browser bundle.

Every request function must resolve to {data: unknown, ...} (an axios-response-shaped object). Whatever data is gets returned as-is by useQueryHandler/useMutationHandler/prefetchQueries — no envelope assumptions. If your backend wraps responses, unwrap inside your adapter before returning.

2. Wrap your app

'use client';

import {ReactQueryProvider} from '@desolint/tanstack-query';

import {reactQueryConfig} from './reactQueryConfig.client';

export function Providers({children}) {
  return (
    <ReactQueryProvider
      config={reactQueryConfig}
      devtools={process.env.NODE_ENV === 'development'}
    >
      {children}
    </ReactQueryProvider>
  );
}

Pass devtools to mount TanStack's devtools panel — the package renders <ReactQueryDevtools> for you from its own @tanstack/react-query instance, so you never import @tanstack/react-query-devtools yourself and its version stays in lockstep with the query client. It no-ops in production builds. @tanstack/react-query-devtools is an optional peer dependency.

3. Queries and mutations

See examples/basic-query.tsx and examples/basic-mutation.tsx. endpoint may be a string or a builder (params) => string; when you pass params, the same value builds both the URL and the cache key, so they can't drift.

4. SSR prefetch (Next.js App Router)

See examples/ssr-prefetch.tsx. prefetchQueries / ReactPrefetchQueryProvider take config and cookieHeader explicitly — resolve the cookie header yourself ((await cookies()).toString()) so this package has no dependency on Next.js. Each QueryToPrefetch carries its own params, keyed identically to the client hook.

.

The client entry point. Everything below is imported from @desolint/tanstack-query.

ReactQueryProvider

| Prop | Type | Notes | | ---------- | ------------------ | -------------------------------------------------------------- | | config | ReactQueryConfig | Required. The adapter object from step 1. | | children | ReactNode | Required. | | devtools | boolean | Mounts TanStack's devtools panel. No-ops in production builds. |

Render the devtools through this prop rather than importing @tanstack/react-query-devtools yourself — importing it separately can resolve a second copy of @tanstack/react-query and throw No QueryClient set.

Hooks

useQueryHandler<TData>({queryKey, endpoint, params?, enabled?, customQueryOptions?, callbacks?, refetchConfig?, mergedToastConfig?})

Wraps useQuery. endpoint may be a string or (params) => string; when you pass params, the same value builds both the URL and the cache key, so the two cannot drift. refetchConfig.mode: 'live' polls every refreshInSeconds (default 30).

useMutationHandler<TData, TParams>({endpoint, method, callBackFuncs?, queriesToChangeAlways?, toastConfigAlways?})

Wraps useMutation. method selects the function to use from config.requestFunctions (POST, PUT, PATCH, DELETE). Invalidates and refetches the queries named in queriesToChange* after a successful mutation.

useInvalidateAndRefetchQuery()

Returns {handleQueries} for invalidating or refetching query keys by hand.

Utilities

getQueryClient(), buildQueryKey(), createRefetchOptions(), processToastConfig(), extractResponseMessage(), safeMerge().

Constants

| Constant | What it is | | ----------------------------- | --------------------------------------------------------------------------------- | | QUERY_TIMINGS | STALE_TIME (30 min), GC_TIME (30 min), MAX_RETRY_DELAY, REFETCH_INTERVAL. | | QUERY_RETRY_CONFIG | Retry policy. Does not retry a 401; otherwise retries twice. | | DEFAULT_QUERY_OPTIONS | The defaults ReactQueryProvider applies. | | REFETCH_MODES | 'static' | 'live'. | | extractErrorStatus({error}) | Reads an HTTP status from an axios, fetch, or bare-envelope error shape. |

/server

SSR only, imported from @desolint/tanstack-query/server. Kept in a separate entry point so server code never lands in the client bundle.

prefetchQueries({config, queriesToFetch, cookieHeader})Promise<QueryClient>

Throws if config.getServerRequest is missing. Each QueryToPrefetch carries its own params, keyed identically to the client hook, so a prefetched query resolves from cache on the client instead of refetching.

ReactPrefetchQueryProvider — dehydrates the prefetched client to the browser.

Both take config and cookieHeader explicitly; resolve the cookie header yourself ((await cookies()).toString()) so this package never depends on Next.js.

Full type definitions ship in dist/index.d.ts and dist/server.d.ts.

Behaviour worth knowing

  • Every request function must resolve to {data: unknown, ...} (an axios-response-shaped object). Whatever data holds is returned as-is — no envelope assumptions. Unwrap inside your adapter if your backend wraps responses.
  • Side effects (onSuccess, onError, toasts) fire once per settled fetch, never once per retry attempt.
  • There is no global setup call. Config lives in React context, so the client and server graphs never share or race on state.

Development

npm install       # install dependencies (Husky wires up git hooks)
npm run build     # bundle to dist/
npm run type-check # tsc --noEmit
npm test          # vitest
npm run lint      # eslint

A pre-commit hook runs lint-staged over staged files, and the same lint runs in CI on every push and PR.


License

MIT © Desolint — see LICENSE.

Free to use, modify and redistribute, commercially or otherwise. Provided "as is", without warranty or liability of any kind.