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

@quanticjs/react-query

v7.3.1

Published

React Query integration for QuanticJS — typed hooks with auto client injection, smart defaults, pagination

Downloads

334

Readme

@quanticjs/react-query

TanStack Query hooks with auto-injected API client, typed errors, and declarative cache invalidation.

Install

pnpm add @quanticjs/react-core @quanticjs/react-query @tanstack/react-query

Setup

import { QuanticProvider } from '@quanticjs/react-core';
import { QuanticQueryProvider } from '@quanticjs/react-query';

<QuanticProvider client={apiClient}>
  <QuanticQueryProvider>
    <App />
  </QuanticQueryProvider>
</QuanticProvider>

QuanticQueryProvider creates a QueryClient with smart defaults. To override:

import { createQueryClient } from '@quanticjs/react-query';

const queryClient = createQueryClient({
  defaultOptions: { queries: { staleTime: 60_000 } },
});

<QuanticQueryProvider client={queryClient}>

Default Query Options

| Option | Value | Rationale | |---|---|---| | retry | No retry on 4xx, 2 retries on 5xx | Client errors won't self-heal | | retryDelay | ApiError.retryAfter * 1000 when set, else TanStack default backoff | Honors server Retry-After under rate limiting | | staleTime | 30 seconds | Prevents redundant fetches | | refetchOnWindowFocus | false | Opt-in, not opt-out | | Mutation retry | false | Mutations should not auto-retry |

useApiQuery

Drop-in wrapper around useQuery that auto-injects the API client from QuanticProvider.

import { useApiQuery } from '@quanticjs/react-query';

const { data, isLoading, error } = useApiQuery(
  ['projects', id],
  (client) => client.get<Project>(`/projects/${id}`),
);

Signature

function useApiQuery<T>(
  key: QueryKey,
  fn: (client: ApiClient) => Promise<T>,
  options?: Omit<UseQueryOptions<T, ApiError>, 'queryKey' | 'queryFn'>,
): UseQueryResult<T, ApiError>
  • key — TanStack Query cache key
  • fn — receives the API client, returns a promise
  • options — any UseQueryOptions except queryKey/queryFn (e.g., enabled, staleTime, retry, select)

The error is always typed as ApiError — no casting needed.

Examples

// Conditional fetching
const { data } = useApiQuery(
  ['projects', id],
  (client) => client.get<Project>(`/projects/${id}`),
  { enabled: !!id },
);

// Custom stale time
const { data } = useApiQuery(
  ['config'],
  (client) => client.get<Config>('/config'),
  { staleTime: 5 * 60 * 1000 },
);

// With query params
const { data } = useApiQuery(
  ['items', { search, page }],
  (client) => client.get<Items>('/items', { params: { search, page } }),
);

useApiMutation

Wrapper around useMutation with auto-injected client and declarative cache invalidation.

import { useApiMutation } from '@quanticjs/react-query';

const mutation = useApiMutation(
  (client, dto: CreateProjectDto) => client.post<Project>('/projects', dto),
  {
    invalidates: [['projects']],
    onSuccess: (project) => navigate(`/projects/${project.id}`),
  },
);

// Usage
mutation.mutate({ name: 'My Project' });

Signature

function useApiMutation<TData, TVariables = void>(
  fn: (client: ApiClient, variables: TVariables) => Promise<TData>,
  options?: ApiMutationOptions<TData, TVariables>,
)

ApiMutationOptions

| Property | Type | Description | |---|---|---| | invalidates | QueryKey[] | Query keys to invalidate on success | | onSuccess | (data, variables) => void | Called after mutation + invalidation | | onError | (error: ApiError, variables) => void | Called on failure | | onSettled | (data, error, variables) => void | Called regardless of outcome | | retry | boolean \| number | Override default retry behavior |

Cache invalidation runs automatically before onSuccess — queries are already refetching by the time your callback fires.

Examples

// No variables (void)
const logout = useApiMutation(
  (client) => client.post<void>('/auth/logout'),
);
logout.mutate();

// File upload
const upload = useApiMutation(
  (client, file: File) => {
    const form = new FormData();
    form.append('file', file);
    return client.upload<UploadResult>('/files', form);
  },
  { invalidates: [['files']] },
);

// Error handling
const save = useApiMutation(
  (client, dto: UpdateDto) => client.put(`/items/${id}`, dto),
  {
    invalidates: [['items']],
    onError: (error) => {
      if (error.isConflict) toast.error('Item was modified by someone else');
    },
  },
);

usePaginatedQuery

Manages page state internally with smooth transitions via keepPreviousData.

import { usePaginatedQuery } from '@quanticjs/react-query';

const {
  data,           // T[] — current page items
  total,          // number — total item count
  page,           // number — current page (1-based)
  totalPages,     // number
  isLoading,
  isFetching,     // true during background refetch (page transition)
  nextPage,       // () => void
  prevPage,       // () => void
  hasNextPage,    // boolean
  hasPrevPage,    // boolean
  setPage,        // (page: number) => void
} = usePaginatedQuery(
  ['items'],
  (client, { page, limit }) =>
    client.get<PaginatedResponse<Item>>('/items', { params: { page, limit } }),
  { limit: 25 },
);

Server Response Format

Your API must return:

interface PaginatedResponse<T> {
  data: T[];
  total: number;
}

Options

| Property | Type | Default | Description | |---|---|---|---| | limit | number | 20 | Items per page | | enabled | boolean | true | Enable/disable the query |

Exports

// Provider
QuanticQueryProvider, createQueryClient, type QuanticQueryProviderProps

// Hooks
useApiQuery
useApiMutation, type ApiMutationOptions
usePaginatedQuery, type PaginatedResponse, type UsePaginatedQueryOptions