Skip to main content
The useSuspenseQuery hook is a specialized version of useQuery designed for React Suspense. It always suspends while fetching and throws errors to error boundaries, providing a more streamlined type signature.

Import

import { useSuspenseQuery } from '@tanstack/react-query'

Signature

function useSuspenseQuery<
  TQueryFnData = unknown,
  TError = DefaultError,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey,
>(
  options: UseSuspenseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
  queryClient?: QueryClient,
): UseSuspenseQueryResult<TData, TError>

Parameters

options
object
required
The query options object.
queryClient
QueryClient
Override the default QueryClient.
The following options are NOT available in useSuspenseQuery because they are automatically set:
  • enabled - Always true
  • suspense - Always true
  • throwOnError - Always throws errors to error boundaries
  • placeholderData - Not supported

Returns

UseSuspenseQueryResult<TData, TError>
object
The query result object. Data is always defined (never undefined).

Examples

Basic Usage

import { Suspense } from 'react'
import { useSuspenseQuery } from '@tanstack/react-query'

function Todo({ id }: { id: number }) {
  const { data } = useSuspenseQuery({
    queryKey: ['todo', id],
    queryFn: async () => {
      const response = await fetch(`/api/todos/${id}`)
      return response.json()
    },
  })

  // data is always defined, no need to check for undefined
  return <div>{data.title}</div>
}

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Todo id={1} />
    </Suspense>
  )
}

With Error Boundary

import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { useSuspenseQuery } from '@tanstack/react-query'

function Todos() {
  const { data } = useSuspenseQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  })

  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

function App() {
  return (
    <ErrorBoundary fallback={<div>Error loading todos</div>}>
      <Suspense fallback={<div>Loading...</div>}>
        <Todos />
      </Suspense>
    </ErrorBoundary>
  )
}

Multiple Suspense Queries

function TodoDetails({ id }: { id: number }) {
  const { data: todo } = useSuspenseQuery({
    queryKey: ['todo', id],
    queryFn: () => fetchTodo(id),
  })

  const { data: comments } = useSuspenseQuery({
    queryKey: ['comments', id],
    queryFn: () => fetchComments(id),
  })

  return (
    <div>
      <h1>{todo.title}</h1>
      <div>
        {comments.map(comment => (
          <p key={comment.id}>{comment.text}</p>
        ))}
      </div>
    </div>
  )
}

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <TodoDetails id={1} />
    </Suspense>
  )
}

With Select

interface Todo {
  id: number
  title: string
  completed: boolean
}

function CompletedTodos() {
  const { data: completedTodos } = useSuspenseQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    select: (todos: Todo[]) => todos.filter(todo => todo.completed),
  })

  return (
    <ul>
      {completedTodos.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

Nested Suspense Boundaries

function UserProfile({ userId }: { userId: number }) {
  const { data: user } = useSuspenseQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  })

  return (
    <div>
      <h1>{user.name}</h1>
      <Suspense fallback={<div>Loading posts...</div>}>
        <UserPosts userId={userId} />
      </Suspense>
    </div>
  )
}

function UserPosts({ userId }: { userId: number }) {
  const { data: posts } = useSuspenseQuery({
    queryKey: ['posts', userId],
    queryFn: () => fetchPosts(userId),
  })

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

function App() {
  return (
    <ErrorBoundary fallback={<div>Error</div>}>
      <Suspense fallback={<div>Loading user...</div>}>
        <UserProfile userId={1} />
      </Suspense>
    </ErrorBoundary>
  )
}

Notes

useSuspenseQuery requires React’s Suspense and Error Boundary to be set up in parent components.
The data property is always defined (never undefined) when the component renders, providing better type safety.
Errors are automatically thrown to the nearest error boundary, so you don’t need to handle error states manually.
The skipToken cannot be used with useSuspenseQuery. Use regular useQuery with enabled: false if you need conditional fetching.

Build docs developers (and LLMs) love