Skip to content

Comparison ​

Use the Framework picker — the matrix below is core; stack-specific notes appear under each framework.

tanstack-fetch is an HTTP client shaped for TanStack Query. axios, ky, and ofetch can do the same network work — the difference is the call site.

Feature matrix ​

Needtanstack-fetchaxioskyofetch
Drop into TanStack Query queryFnReturns data, throws FetchError, takes signalAdaptAdaptAdapt
Typed error with status / bodyFetchError + isFetchErrorPartialPartialPartial
Handlers for 401–429First-classCustomCustomCustom
Refresh token + single-flightInterceptor + { action: 'retry' }CustomCustomCustom
Next.js SSR cookiesssr-forward pluginCustomCustomCustom
SSE with Authorizationtanstack-fetch/sseNoNoNo
WebSocket / WebTransport with authapi.ws / api.transport (/live)NoNoNo
NDJSON streaming (POST + headers)api.stream (/live)CustomCustomCustom
File upload + progressapi.upload() (/sse)YesLimitedLimited
tRPC transporttanstack-fetch/trpcNoNoNo
Bundle3.57KB gzip HTTP coreLargerSmallSmall

Before / after ​

axios into Query ​

ts
useQuery({
  queryKey: ['users'],
  queryFn: async ({ signal }) => {
    const response = await axios.get('/users', { signal })
    return response.data
  },
})

tanstack-fetch into Query ​

ts
useQuery({
  queryKey: ['users'],
  queryFn: ({ signal }) => api.get<User[]>('/users', { signal }),
})

No .data unwrap. HTTP failures throw, so Query’s isError path stays honest.

Migrating? CLI + mapping for axios, ky, ofetch, and raw fetch: Migrate.

Refresh token ​

ts
import { createRefreshTokenInterceptor } from 'tanstack-fetch/plugins'

api.use(
  'refresh-token',
  createRefreshTokenInterceptor({
    refresh: async () => {
      const body = await api.post<{
        accessToken: string
        expiresIn: number
      }>('/auth/refresh', {
        interceptors: { eject: ['refresh-token', 'auth'] },
      })
      /* persist body.accessToken + expiresAt */
    },
    before: {
      getExpiresAt: () => expiresAt,
      skewMs: 60_000,
    },
    after: { enabled: true },
  }),
)

Full recipe: Refresh token.

SSR cookies ​

Next.js App Router fetch does not forward cookies automatically. ssr-forward plugin reads cookies() on the server and attaches them.

ts
// server
import { cookies } from 'next/headers'
import { createSsrForwardInterceptor } from 'tanstack-fetch/plugins'
export const api = createFetch({
  baseUrl: process.env.API_URL,
  source: 'ssr',
  interceptors: [createSsrForwardInterceptor()],
  incoming: () => ({ cookie: (await cookies()).toString() }),
})

// client
const api = createFetch({ baseUrl: '/api', interceptors: [createSsrForwardInterceptor()] })

Guide: SSR · Blog: Next.js fetch vs tanstack-fetch · Example: /examples/next-ssr.

SSE with Authorization ​

Browser EventSource cannot set Authorization. Use tanstack-fetch/sse (api.sse) instead.

ts
import { createFetch } from 'tanstack-fetch/sse'

const api = createFetch({
  baseUrl: import.meta.env.VITE_API_URL,
  getToken: () => localStorage.getItem('access_token'),
})

api.sse<OrderEvent>('/orders/stream', {
  onMessage: (data) => queryClient.setQueryData(['orders'], (prev) => [...prev, data]),
})

WebSocket, WebTransport & NDJSON ​

axios, ky and ofetch stop at request / response. For sockets you'd usually add a second library and wire auth into it separately. tanstack-fetch/live reuses the same client:

ts
import { createFetch } from 'tanstack-fetch/live'
import { createRefreshTokenInterceptor } from 'tanstack-fetch/plugins'

const api = createFetch({ baseUrl: '/api', getToken })
api.use('refresh-token', createRefreshTokenInterceptor({ refresh }))

const room = api.ws<ChatMessage, { id: string }>('/rooms/:id', { params: { id }, authIn: 'query' })
for await (const chunk of api.stream<Token>('/chat', { body: { prompt } })) render(chunk)

When to keep axios ​

  • You already have a large axios interceptor stack and no Query migration planned
  • You need axios-specific adapters that are not fetch-based

Otherwise, if TanStack Query is the source of truth for loading and errors, tanstack-fetch matches that model with less glue.

React / Query ​

Use @tanstack/react-query + tanstack-fetch/react. Guide: React · TanStack Query.

Vue / Nuxt ​

Use @tanstack/vue-query + tanstack-fetch/vue. Guide: Vue.

Solid ​

Use @tanstack/solid-query + tanstack-fetch/solid. Guide: Solid.

Angular ​

Use @tanstack/angular-query-experimental + tanstack-fetch/angular. Guide: Angular. Deep dive: HttpClient vs tanstack-fetch.

Svelte ​

Use @tanstack/svelte-query + tanstack-fetch/svelte. Guide: Svelte.

Next.js ​

SSR cookies via ssr-forward. Guide: SSR · Next.js.

TanStack Start ​

Router context + Query. TanStack Query · tRPC recipe.

Remix ​

Loaders + optional Query hydration. tRPC · Remix.

MIT Licensed · Created by Mohammad Garmabi · Not an official TanStack package