TanStack
Guides & Concepts

Suspense

Preact Query can also be used with Preact's Suspense for Data Fetching APIs. For this, we have dedicated hooks:

When using suspense mode, status states and error objects are not needed and are then replaced by usage of the Suspense component from preact/compat (including the use of the fallback prop and error boundaries for catching errors). Please read the Resetting Error Boundaries section for more information on how to set up suspense mode.

If you want mutations to propagate errors to the nearest error boundary (similar to queries), you can set the throwOnError option to true as well.

Enabling suspense mode for a query:

tsx
import { useSuspenseQuery } from '@tanstack/preact-query'

const { data } = useSuspenseQuery({ queryKey, queryFn })

This works nicely in TypeScript, because data is guaranteed to be defined (as errors and loading states are handled by Suspense- and ErrorBoundaries).

On the flip side, you therefore can't conditionally enable / disable the Query. This generally shouldn't be necessary for dependent Queries because with suspense, all your Queries inside one component are fetched in serial.

placeholderData also doesn't exist for this Query.

throwOnError default

Not all errors are thrown to the nearest Error Boundary per default - we're only throwing errors if there is no other data to show. That means if a Query ever successfully got data in the cache, the component will render, even if data is stale. Thus, the default for throwOnError is:

plaintext
throwOnError: (error, query) => typeof query.state.data === 'undefined'

Since you can't change throwOnError (because it would allow for data to become potentially undefined), you have to throw errors manually if you want all errors to be handled by Error Boundaries:

tsx
import { useSuspenseQuery } from '@tanstack/preact-query'

const { data, error, isFetching } = useSuspenseQuery({ queryKey, queryFn })

if (error && !isFetching) {
  throw error
}

// continue rendering data

Resetting Error Boundaries

Whether you are using suspense or throwOnError in your queries, you will need a way to let queries know that you want to try again when re-rendering after some error occurred.

Query errors can be reset with the QueryErrorResetBoundary component or with the useQueryErrorResetBoundary hook.

When using the component it will reset any query errors within the boundaries of the component:

Preact does not ship an error boundary component, but one can be built with the useErrorBoundary hook:

tsx
import { QueryErrorResetBoundary } from '@tanstack/preact-query'
import { useErrorBoundary } from 'preact/hooks'

function ErrorBoundary({ children, onReset, fallbackRender }) {
  const [error, resetError] = useErrorBoundary()

  if (error) {
    return fallbackRender({
      error,
      resetErrorBoundary: () => {
        onReset()
        resetError()
      },
    })
  }

  return children
}

const App = () => (
  <QueryErrorResetBoundary>
    {({ reset }) => (
      <ErrorBoundary
        onReset={reset}
        fallbackRender={({ resetErrorBoundary }) => (
          <div>
            There was an error!
            <button onClick={() => resetErrorBoundary()}>Try again</button>
          </div>
        )}
      >
        <Page />
      </ErrorBoundary>
    )}
  </QueryErrorResetBoundary>
)

When using the hook it will reset any query errors within the closest QueryErrorResetBoundary. If there is no boundary defined it will reset them globally:

tsx
import { useQueryErrorResetBoundary } from '@tanstack/preact-query'

const App = () => {
  const { reset } = useQueryErrorResetBoundary()
  return (
    <ErrorBoundary
      onReset={reset}
      fallbackRender={({ resetErrorBoundary }) => (
        <div>
          There was an error!
          <button onClick={() => resetErrorBoundary()}>Try again</button>
        </div>
      )}
    >
      <Page />
    </ErrorBoundary>
  )
}

Fetch-on-render vs Render-as-you-fetch

Out of the box, Preact Query in suspense mode works really well as a Fetch-on-render solution with no additional configuration. This means that when your components attempt to mount, they will trigger query fetching and suspend, but only once you have imported them and mounted them. If you want to take it to the next level and implement a Render-as-you-fetch model, we recommend implementing Prefetching on routing callbacks and/or user interactions events to start loading queries before they are mounted and hopefully even before you start importing or mounting their parent components.