Suspense & Data Loading
HardSuspense is a React component that displays a fallback while its children are loading. Originally for code splitting with React.lazy, Suspense now supports data fetching in React 18+ through frameworks like Next.js. It provides a declarative way to handle async loading states without manual isLoading flags.
Interactive Visualization
Key Points
- Suspense shows a fallback prop while children are suspended
- Works with React.lazy for code splitting
- Works with data fetching in frameworks like Next.js via Server Components
- Eliminates manual loading state management for supported data sources
- Nested Suspense boundaries control loading granularity
- SuspenseList (experimental) coordinates reveal order of multiple suspended items
Code Examples
Nested Suspense Boundaries
import { Suspense, lazy } from 'react' const UserProfile = lazy(() => import('./UserProfile')) const UserPosts = lazy(() => import('./UserPosts')) const Sidebar = lazy(() => import('./Sidebar')) function UserPage() { return ( <div> {/* Outer boundary: entire page skeleton */} <Suspense fallback={<PageSkeleton />}> <UserProfile /> {/* Inner boundary: posts load independently */} <Suspense fallback={<PostsSkeleton />}> <UserPosts /> </Suspense> {/* Another independent boundary */} <Suspense fallback={<SidebarSkeleton />}> <Sidebar /> </Suspense> </Suspense> </div> ) } function PageSkeleton() { return <div>Loading page...</div> } function PostsSkeleton() { return <div>Loading posts...</div> } function SidebarSkeleton() { return <div>Loading sidebar...</div> }
Each Suspense boundary independently shows its fallback, letting the profile load and display while posts and sidebar are still loading
Suspense with Server Components (Next.js)
import { Suspense } from 'react' // Server Component that fetches data async function UserData({ userId }: { userId: string }) { const user = await fetch(`/api/users/${userId}`).then( (r) => r.json() as Promise<{ name: string; bio: string }> ) return ( <div> <h2>{user.name}</h2> <p>{user.bio}</p> </div> ) } // Parent wraps the async component in Suspense function UserPage({ userId }: { userId: string }) { return ( <div> <h1>User Profile</h1> <Suspense fallback={<div>Loading user data...</div>}> <UserData userId={userId} /> </Suspense> </div> ) }
In the Server Components model, async components suspend automatically while awaiting data, and Suspense provides the loading UI
Common Mistakes
- Placing the Suspense boundary too high, showing a single spinner for the entire page
- Not providing meaningful skeleton fallbacks that match the content layout
- Expecting Suspense to work with arbitrary promises without framework support
Interview Tips
- Explain the evolution: Suspense for code splitting first, then for data fetching
- Discuss how Suspense eliminates waterfall loading with parallel data fetching
- Know that Suspense for data fetching requires framework integration, not raw fetch