React
Framework
React · tanstack-fetch/react · @tanstack/react-query
Optional helpers from tanstack-fetch/react.
Live demo
Live demo · React / TanStack Query
useQuery + useMutation shape
Real createFetch calls — same queryFn / mutationFn contract your React app uses.
idle — click useQuery
const api = createFetch({ baseUrl })
// React Query
useQuery({
queryKey: ['users'],
queryFn: ({ signal }) => api.get<User[]>('/users', { signal }),
})
useMutation({
mutationFn: (body) => api.post<User>('/users', { body }),
})FetchProvider
Optional — only if you want useFetch() from context. For SSE you can skip it and pass client to useSse.
import { FetchProvider, useFetch } from 'tanstack-fetch/react'
import { useQuery } from '@tanstack/react-query'
import { isFetchError } from 'tanstack-fetch'
const App = () => (
<FetchProvider
baseUrl={import.meta.env.VITE_API_URL}
getToken={() => localStorage.getItem('access_token')}
onUnauthorized={() => {
localStorage.removeItem('access_token')
window.location.href = '/login'
}}
plugins={['trace', 'retry-idempotent']}
>
<UsersPage />
</FetchProvider>
)
const UsersPage = () => {
const api = useFetch()
const { data, error, isPending } = useQuery({
queryKey: ['users'],
queryFn: ({ signal }) => api.get<User[]>('/users', { signal }),
})
if (isPending) return <p>Loading…</p>
if (isFetchError(error))
return (
<p>
{error.status}: {error.message}
</p>
)
return (
<ul>
{data.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
)
}useSse
Create the client from tanstack-fetch/sse, then pass it as { client: api }. FetchProvider is not required.
status: 'connecting' | 'connected' | 'disconnected' | 'error'
Live demo
Live demo · useSse
React useSse + status
Pass {'{ client: api }'} — no FetchProvider required. Status: connecting → connected → disconnected / error.
status: disconnected
import { createFetch } from 'tanstack-fetch/sse'
import { useSse } from 'tanstack-fetch/react'
const api = createFetch({
baseUrl: import.meta.env.VITE_API_URL,
plugins: ['sse-resume'],
getToken: () => localStorage.getItem('access_token'),
})
const OrdersLive = () => {
// Pass client — FetchProvider is optional
const { data, status, error, close } = useSse<Tick>('/orders/stream', {
client: api,
})
if (status === 'error') return <p>{error?.message}</p>
return (
<div>
<p>{status}</p>
<p>{data ? `#${data.n}` : '—'}</p>
<button type="button" onClick={close}>Disconnect</button>
</div>
)
}Recommended (no provider)
import { createFetch } from 'tanstack-fetch/sse'
import { useSse } from 'tanstack-fetch/react'
type OrderEvent = { id: string; status: string }
const api = createFetch({
baseUrl: import.meta.env.VITE_API_URL,
plugins: ['sse-resume'],
getToken: () => localStorage.getItem('access_token'),
})
const OrdersLive = () => {
const { data, status, error, close } = useSse<OrderEvent>('/orders/stream', {
client: api,
onMessage: (payload) => console.log('tick', payload),
})
if (status === 'error') return <p>Stream failed: {error?.message}</p>
return (
<div>
<p>status: {status}</p>
<p>{data ? `${data.id} · ${data.status}` : 'waiting…'}</p>
<button type="button" onClick={close} disabled={status === 'disconnected'}>
Disconnect
</button>
</div>
)
}useWs
Create the client from tanstack-fetch/live and pass { client: api }. Same result shape as useSse, plus send. In the browser, set authIn ('query' or 'protocol') when you have a token. Add transport: 'webtransport' for WebTransport. See WebSocket, WebTransport & streams.
import { createFetch } from 'tanstack-fetch/live'
import { useWs } from 'tanstack-fetch/react'
type ChatMessage = { user: string; text: string }
const api = createFetch({
baseUrl: import.meta.env.VITE_API_URL,
getToken: () => localStorage.getItem('access_token'),
})
const ChatRoom = ({ id }: { id: number }) => {
const { data, status, error, send, close } = useWs<ChatMessage, { id: string }>('/rooms/:id', {
client: api,
params: { id },
authIn: 'query',
})
if (status === 'error') return <p>Socket error: {String(error)}</p>
return (
<div>
<p>status: {status}</p>
<p>{data?.text ?? 'waiting…'}</p>
<button type="button" onClick={() => send({ text: 'hi' })}>
Send
</button>
<button type="button" onClick={close}>
Disconnect
</button>
</div>
)
}Optional: via FetchProvider
If the tree already has <FetchProvider client={api}>, you can omit client and useSse reads it from context.
Cloneable app: examples/sse-live · SSE guide
