React Suspense components must have a fallback prop or be nested inside another Suspense boundary. Without a fallback, React cannot display a loading state while suspended content loads.
This error occurs when you use a React Suspense component without providing a fallback prop, and the component is not nested inside another Suspense boundary that has a fallback. The fallback prop is essential because it tells React what UI to display while the suspended content (such as lazy-loaded components or data being fetched) is loading. React Suspense is designed to handle asynchronous operations gracefully by showing a placeholder UI during loading. When a component inside a Suspense boundary suspends (delays rendering while waiting for something), React looks for the nearest Suspense boundary with a fallback to display. If no fallback is found anywhere up the component tree, React cannot proceed and throws this error. In React 18 and later, the behavior was updated to be more strict. Previously, React would silently skip Suspense boundaries without fallbacks and look for the next one up the tree. This led to confusing debugging situations where adding a Suspense boundary wouldn't change behavior if you forgot the fallback prop. Now React explicitly requires the fallback prop or nesting within another boundary.
The most direct fix is to add a fallback prop with a valid React element. The fallback can be any JSX, but typically you'll use a loading spinner, skeleton, or placeholder text:
import { Suspense } from 'react';
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<YourAsyncComponent />
</Suspense>
);
}For a better user experience, use a more polished loading indicator:
import { Suspense } from 'react';
function App() {
return (
<Suspense fallback={
<div className="flex items-center justify-center p-8">
<div className="animate-spin h-8 w-8 border-4 border-blue-500 border-t-transparent rounded-full" />
</div>
}>
<LazyComponent />
</Suspense>
);
}If you have a parent Suspense boundary with a fallback, child Suspense components can be nested without their own fallback:
function App() {
return (
<Suspense fallback={<AppLevelLoader />}>
<Header />
<MainContent />
{/* This Suspense can have its own fallback or inherit from parent */}
<Suspense fallback={<SectionLoader />}>
<AsyncSection />
</Suspense>
</Suspense>
);
}However, it's best practice to always provide explicit fallbacks for clarity and to avoid confusion when refactoring component trees.
When using React.lazy() for dynamic imports, always wrap the lazy component in a Suspense boundary:
import { Suspense, lazy } from 'react';
const LazyDashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<div>Loading Dashboard...</div>}>
<LazyDashboard />
</Suspense>
);
}For multiple lazy components, you can use a single Suspense boundary or nest them:
const LazyProfile = lazy(() => import('./Profile'));
const LazySettings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/profile" element={<LazyProfile />} />
<Route path="/settings" element={<LazySettings />} />
</Routes>
</Suspense>
);
}If you're using libraries like SWR or React Query that support Suspense mode, ensure Suspense boundaries are properly configured:
For SWR:
import useSWR from 'swr';
import { Suspense } from 'react';
function Profile() {
// suspense: true makes this hook suspend
const { data } = useSWR('/api/user', fetcher, { suspense: true });
return <div>{data.name}</div>;
}
function App() {
return (
<Suspense fallback={<div>Loading profile...</div>}>
<Profile />
</Suspense>
);
}For React Query (TanStack Query):
import { useSuspenseQuery } from '@tanstack/react-query';
import { Suspense } from 'react';
function Posts() {
const { data } = useSuspenseQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
});
return <PostList posts={data} />;
}
function App() {
return (
<Suspense fallback={<div>Loading posts...</div>}>
<Posts />
</Suspense>
);
}Check that you're using a React version that supports your Suspense use case:
npm list react react-dom- React 16.6+: Suspense for code splitting (React.lazy) only
- React 18+: Suspense for data fetching with concurrent features
If you're on React 16 or 17 and trying to use Suspense for data fetching, you'll need to upgrade:
npm install react@latest react-dom@latestAfter upgrading to React 18, update your root rendering:
// React 18 with concurrent features
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(
<Suspense fallback={<div>Loading app...</div>}>
<App />
</Suspense>
);Suspense Boundary Placement Strategy: Position Suspense boundaries strategically to control loading granularity. Place them close to components that suspend to avoid having entire sections disappear during loading. For complex UIs, use multiple nested boundaries with different fallbacks to create progressive loading experiences.
Suspense vs Error Boundaries: Suspense only catches loading states (suspensions), not errors. To handle errors in components that use Suspense, wrap your Suspense boundary with an Error Boundary. The proper pattern is ErrorBoundary wrapping Suspense, giving you comprehensive error and loading state handling.
SSR and Streaming Considerations: In Next.js and other SSR frameworks using React 18+, Suspense boundaries control streaming behavior. Components inside Suspense boundaries can be sent to the client as they become ready, improving perceived performance. Be aware that fallbacks may flash briefly during hydration if server-rendered content isn't immediately available.
Fallback Prop Behavior Changes: In React 18, the team changed how null or undefined fallbacks are handled. Previously, React would skip boundaries without fallbacks and search upwards. Now it captures the boundary and renders null for the fallback. This change makes debugging easier but means existing code that relied on the old behavior may need updates.
Context Loss Issues: Be cautious when placing Suspense boundaries that might cause important UI elements (headers, sidebars, navigation) to unmount. When a Suspense boundary's fallback is shown, all children are unmounted, potentially losing context or user input state. Consider using smaller, more targeted Suspense boundaries or using useTransition for smoother updates.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite