Skip to main content

suspense

Description: If true, the useFetch hook will use suspense mode.

Default Value: true.

  • How To: Prefer suspense: true for the primary data of a page or widget. This delegates loading UI to the nearest Suspense boundary, keeping your component code clean and promoting declarative loading states.
    // Correct: Leverage Suspense for main content loading
    useFetch(
    'main-content',
    fetchMainContent
    );
  • Best Practice: Avoid setting suspense: false if you intend to use errorBoundary: true, as errorBoundary relies on Suspense to function. If suspense: false, you must manually handle isLoading states within your component.
    // Avoid this: `errorBoundary` won't work without `suspense: true`
    useFetch(
    'data',
    () => fetchData(),
    { suspense: false, errorBoundary: true } // Won't work without suspense
    );

Deep Dive: How suspense works step by step

Example:

function UserList() {
const { data, isLoading } = useFetch('users', () => actionGetUsers(), {
suspense: true // Default
});

if (isLoading || !data) {
return null;
}

return <ul>{data.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}

<Suspense fallback={<LoadingSpinner />}>
<UserList />
</Suspense>

What happens step by step with suspense: true (default):

  1. Component rendersuseFetch is called
  2. No cached data existsuseFetch needs to fetch
  3. useFetch throws a Promise → This is the key behavior!
  4. React Suspense catches the thrown Promise → Stops rendering UserList
  5. <LoadingSpinner /> is displayed → Fallback shows while waiting
  6. Fetch completes → Promise resolves with data
  7. React re-renders UserList → Now data has the users array
  8. Component displays the list → No more Promise thrown

What happens step by step with suspense: false:

  1. Component rendersuseFetch is called
  2. No cached data existsuseFetch needs to fetch
  3. useFetch returns { data: null, isLoading: true } → No Promise thrown
  4. Component continues rendering → Must handle isLoading manually
  5. You must check isLoading → Show your own loading UI
  6. Fetch completesdata updates, isLoading becomes false
  7. Component re-renders → Now displays actual data

Key difference: With suspense: true, the Promise is thrown (error-like behavior), causing React Suspense to take over. With suspense: false, everything is returned as normal values.

The suspension seam (React 19 use())

Suspension is centralized behind a small seam so the mechanism can differ per hook:

  • useFetch (with suspense: true) throws the pending, cache-stable promise — the classic React 18 style behavior.
  • useSuspenseFetch unwraps the promise through the React 19 use() API. use() suspends the render while the promise is pending, integrates with the nearest <Suspense> boundary, and re-throws a rejection to the nearest error boundary.

On runtimes that don't expose use() (older React, Preact), the seam falls back to throwing the promise, which suspends the same way. In both cases the promise must be referentially stable across renders — it comes from the data cache, not created inline — otherwise React would re-suspend forever.

tip

If a component cannot render without its data, reach for useSuspenseFetch instead of useFetch with suspense: true. It forces suspense, errorBoundary and enabled on, guarantees an in-flight request even on a cold cache, and returns a non-null data.