How to fix Cannot use Suspense boundary without an async component in React

ReactINTERMEDIATEMEDIUMUpdated Dec 10, 2025· 135 views

This error occurs when you wrap components in a Suspense boundary but none of the child components actually suspend (throw a Promise). Suspense requires at least one async operation like lazy loading, data fetching with a Suspense-compatible library, or the use() hook in React 19. Understanding how Suspense works and when components properly suspend resolves this issue.

Quick fix

Fixes Cannot use Suspense boundary without an async component

import { Suspense, lazy } from "react";

// Lazy load the component - this makes it suspend
const HeavyComponent = lazy(() => import("./HeavyComponent"));

export function App() {
  return (
    <Suspense fallback={<div>Loading component...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}
  1. Use React.lazy() for code splitting to enable Suspense
  2. Use React 19's use() hook for data fetching
  3. Use Suspense-compatible data fetching libraries
  4. Combine Suspense with Error Boundaries
react — error
import { Suspense, lazy } from "react";
// Lazy load the component - this makes it suspend
const HeavyComponent = lazy(() => import("./HeavyComponent"));
export function App() {
return (
<Suspense fallback={<div>Loading component...</div>}>
<HeavyComponent />
</Suspense>
);
}
Cannot use Suspense boundary without an async component

What this error means

React Suspense is designed to handle asynchronous operations by displaying a fallback UI while waiting for content to load. However, Suspense only activates when a child component "suspends" by throwing a Promise during rendering. If you wrap regular synchronous components in a Suspense boundary without any actual async operations, React may warn or fail because there is nothing to suspend. The error indicates that your Suspense boundary contains only components that render synchronously without triggering the suspension mechanism. Suspense is not just a loading state wrapper—it requires specific integration with async patterns like React.lazy(), data-fetching libraries that support Suspense, or React 19's use() hook.

Typical symptoms

  • Error message stating "Cannot use Suspense boundary without an async component"
  • Suspense fallback never appears even though it is defined
  • Components render immediately without showing loading states
  • Console warnings about improper Suspense usage
  • Suspense boundary acting as if it is not present
  • Loading indicators not displaying when expected during data fetching
  • React development mode warnings about Suspense boundaries with no suspending children

Common causes

  1. Wrapping regular synchronous components in Suspense without any lazy loading or async operations
  2. Using data-fetching methods that do not support Suspense (like traditional useEffect with fetch)
  3. Forgetting to import components with React.lazy() when attempting code splitting
  4. Using Suspense with libraries that do not implement the Suspense protocol (older versions of React Query, SWR without proper configuration)
  5. Attempting to use Suspense for data fetching in React 18 without using frameworks like Next.js or libraries that support Suspense
  6. Not wrapping async components with Error Boundaries, causing errors to break the Suspense flow
  7. Using async/await syntax in components without properly throwing Promises for Suspense to catch
  8. Misunderstanding that Suspense requires components to throw Promises, not just be async functions

How to fix "Cannot use Suspense boundary without an async component"

1Use React.lazy() for code splitting to enable Suspense

The most common and straightforward use of Suspense is with React.lazy() for code splitting. Lazy-loaded components automatically suspend while their code is being fetched. Wrap the lazy component in a Suspense boundary with a fallback.

typescript
import { Suspense, lazy } from "react";

// Lazy load the component - this makes it suspend
const HeavyComponent = lazy(() => import("./HeavyComponent"));

export function App() {
  return (
    <Suspense fallback={<div>Loading component...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}
2Use React 19's use() hook for data fetching

React 19 introduced the use() hook which allows you to read Promises directly in render. When you call use() with a Promise, the component suspends until the Promise resolves. This is the official way to integrate Suspense with async data.

typescript
import { Suspense, use } from "react";

function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  // Calling use() suspends the component until userPromise resolves
  const user = use(userPromise);
  
  return <div>Welcome, {user.name}</div>;
}

export function App() {
  const userPromise = fetchUser();
  
  return (
    <Suspense fallback={<div>Loading user...</div>}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

Note: The use() hook is only available in React 19+.

3Use Suspense-compatible data fetching libraries

Libraries like React Query, SWR, and Relay support Suspense mode. Enable Suspense support in their configuration, and they will automatically throw Promises when data is loading, triggering your Suspense fallback.

typescript
import { Suspense } from "react";
import { useSuspenseQuery } from "@tanstack/react-query";

function UserData() {
  // useSuspenseQuery suspends while fetching
  const { data } = useSuspenseQuery({
    queryKey: ["user"],
    queryFn: fetchUser,
  });
  
  return <div>{data.name}</div>;
}

export function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserData />
    </Suspense>
  );
}

For SWR:

typescript
import useSWR from "swr";

function UserData() {
  const { data } = useSWR("/api/user", fetcher, { suspense: true });
  return <div>{data.name}</div>;
}
4Combine Suspense with Error Boundaries

Suspense only handles loading states. To handle errors in async components, wrap your Suspense boundary with an Error Boundary. This ensures errors during loading do not break your app.

typescript
import { Suspense, Component } from "react";

class ErrorBoundary extends Component {
  state = { hasError: false };
  
  static getDerivedStateFromError() {
    return { hasError: true };
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong</div>;
    }
    return this.props.children;
  }
}

export function App() {
  return (
    <ErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <AsyncComponent />
      </Suspense>
    </ErrorBoundary>
  );
}

Or use a library like react-error-boundary:

typescript
import { ErrorBoundary } from "react-error-boundary";

<ErrorBoundary fallback={<div>Error occurred</div>}>
  <Suspense fallback={<div>Loading...</div>}>
    <AsyncComponent />
  </Suspense>
</ErrorBoundary>
5Do not use Suspense with traditional useEffect data fetching

Traditional data fetching with useEffect and useState does not trigger Suspense. If you are using this pattern, either migrate to a Suspense-compatible library or remove the Suspense boundary and handle loading states manually.

typescript
// WRONG - This does NOT suspend
function UserData() {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetchUser().then(setUser);
  }, []);
  
  if (!user) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

// Wrapping this in Suspense does nothing - remove Suspense or migrate to use()
6Check library versions and Suspense support

Ensure the libraries you use actually support Suspense. Older versions of React Query, SWR, Apollo Client, and other data-fetching libraries may not support Suspense mode.

For React Query, use @tanstack/react-query v4+ and useSuspenseQuery.
For SWR, enable { suspense: true } in configuration.
For Apollo Client, use the @apollo/client v3.8+ with Suspense hooks.

Check library documentation for the correct Suspense integration method.

7Use Next.js server components for server-side Suspense

In Next.js 13+ with App Router, server components can use async/await directly and integrate seamlessly with Suspense. This is the recommended approach for server-side data fetching with Suspense.

typescript
// app/page.tsx (Next.js App Router)
import { Suspense } from "react";

async function UserData() {
  const user = await fetchUser();
  return <div>{user.name}</div>;
}

export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserData />
    </Suspense>
  );
}

Server components that use async/await automatically suspend when used with Suspense.

Advanced notes

Understanding how Suspense works internally helps debug issues. Suspense listens for components that throw Promises during rendering. When a Promise is thrown, React catches it, renders the fallback UI, and waits for the Promise to resolve. Once resolved, React re-renders the component. This "throw and catch" mechanism is why regular async/await functions do not work—they return Promises instead of throwing them. The use() hook in React 19 bridges this gap by handling the Promise throw internally. For React 18 and earlier, you must use libraries that implement this pattern or use React.lazy(). In production applications, consider using frameworks like Next.js that provide first-class Suspense support with Server Components and streaming SSR. Be aware that Suspense is still evolving, and some edge cases with nested Suspense boundaries, concurrent updates, and transitions may behave unexpectedly. Always test Suspense behavior in production builds, as development mode may behave differently. For fine-grained control over loading states, consider using the useTransition() hook alongside Suspense to prevent UI "jank" when navigating between Suspense boundaries.

Frequently asked questions

What does "Cannot use Suspense boundary without an async component" mean?
React Suspense is designed to handle asynchronous operations by displaying a fallback UI while waiting for content to load. However, Suspense only activates when a child component "suspends" by throwing a Promise during rendering. If you wrap regular synchronous components in a Suspense boundary without any actual async operations, React may warn or fail because there is nothing to suspend.
How do I fix "Cannot use Suspense boundary without an async component"?
This error occurs when you wrap components in a Suspense boundary but none of the child components actually suspend (throw a Promise). Suspense requires at least one async operation like lazy loading, data fetching with a Suspense-compatible library, or the use() hook in React 19. Understanding how Suspense works and when components properly suspend resolves this issue.
What causes "Cannot use Suspense boundary without an async component"?
Wrapping regular synchronous components in Suspense without any lazy loading or async operations Using data-fetching methods that do not support Suspense (like traditional useEffect with fetch) Forgetting to import components with React.lazy() when attempting code splitting
Is the "Cannot use Suspense boundary without an async component" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading