Suggest an editImprove this articleRefine the answer for “Suspense and Data Fetching”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`Suspense`** lets React pause rendering part of the tree until a component gets the data it needs, and show fallback UI, and now it can wait not only for `lazy()` components but also for a Promise from a data request. **Key point:** in React 18+, a component or hook throws a Promise, React "freezes" the render of that branch, shows the fallback from the nearest `<Suspense>`, and when the Promise resolves, resumes rendering with the real data.Shown above the full answer for quick recall.Answer (EN)Image## 1. What `Suspense` does while data loads > `Suspense` lets React **pause rendering** part of the tree > until a component **receives the data it needs**, > and show **fallback UI** (for example, a spinner or a skeleton). That is, React can now "wait" not only for `lazy()` components, but also for a **Promise from a data request**. --- ## 2. What changed in React 18 Before React 18: - `Suspense` could only wait for **lazily loaded components** (`React.lazy`); - for data you had to manually show `Loading`, check `isLoading`, handle errors, and so on. With **React 18+**: - `Suspense` can now wait for a **Promise** from any source (fetch, async functions, and so on); - React understands that a component is "not ready yet" and **pauses** its render, showing the `fallback`; - once the Promise resolves -> React **resumes the render** with the real data. --- ## 3. An example of a "manual" implementation with a Promise To understand the mechanics, let's look at a "low-level" example without Next.js: ```javascript // utils/fetchData.js export function fetchData(url) { let status = 'pending'; let result; const suspender = fetch(url) .then(res => res.json()) .then(data => { status = 'success'; result = data; }) .catch(err => { status = 'error'; result = err; }); return { read() { if (status === 'pending') throw suspender; if (status === 'error') throw result; return result; }, }; } ``` Now let's use it in a component: ```javascript const resource = fetchData('/api/user'); function Profile() { const user = resource.read(); // throws a Promise if the data is not ready yet return <h1>{user.name}</h1>; } function App() { return ( <Suspense fallback={<div>Loading profile...</div>}> <Profile /> </Suspense> ); } ``` What happens: 1. `resource.read()` throws a Promise -> React "freezes" the render of the `Profile` component; 2. React shows the `fallback` from `Suspense`; 3. When the Promise resolves -> React "resumes" the render and inserts `Profile` with the data. --- ## 4. Data Fetching in React Server Components (Next.js 13+) In **React 18+** and **Next.js 13/14**, Suspense is deeply integrated into server rendering. ### Example: ```javascript // app/page.js (Next.js 13+) import { Suspense } from 'react'; import UserInfo from './UserInfo'; import UserPosts from './UserPosts'; export default async function Page() { const user = await getUser(); // an async server component return ( <> <h1>{user.name}</h1> <Suspense fallback={<div>Loading posts...</div>}> {/* UserPosts loads independently */} <UserPosts userId={user.id} /> </Suspense> </> ); } ``` Here React: - sends the first part of the HTML (the user's name) right away; - "pauses" the rendering of `UserPosts` until it gets the data; - later **streams in** the HTML for `UserPosts` (Streaming SSR); - all of this happens without reloading the page. This works thanks to new APIs: - `renderToPipeableStream()` - Node.js streaming SSR; - `renderToReadableStream()` - for Deno, Edge, and so on. --- ## 5. Combining Suspense with React Query / SWR React does not yet have a "built-in" fetch mechanism, but popular libraries have already added Suspense support: ```javascript import { useQuery } from '@tanstack/react-query'; import { Suspense } from 'react'; function Todos() { const { data: todos } = useQuery({ queryKey: ['todos'], queryFn: () => fetch('/api/todos').then(res => res.json()), suspense: true, // the key option }); return <ul>{todos.map(t => <li key={t.id}>{t.title}</li>)}</ul>; } export default function App() { return ( <Suspense fallback={<div>Loading tasks...</div>}> <Todos /> </Suspense> ); } ``` `useQuery({ suspense: true })` tells React that it needs to **throw a Promise** while loading, and Suspense automatically displays the `fallback`. --- ## 6. How Suspense works internally 1. A component calls async code (or a hook) that throws a `Promise`; 2. React "encounters" that `Promise`; 3. React: - defers the render of that branch; - shows the `fallback` from the nearest `<Suspense>`; 4. When the Promise resolves, React **resumes** the render; 5. With SSR, React can even **stream in** the finished HTML to the client. --- ## 7. What `Suspense` does not do on its own - It does not perform `fetch()` directly - your code or a library does that. - It does not handle errors (`catch`) - for that you need an `<ErrorBoundary>`. - It does not replace `useEffect` - Suspense works **during rendering**, not **after**. --- ## Summary > **Suspense in React 18** lets React **wait for data the same way it waits for components**. > It turns asynchrony into a **declarative construct**: > React "pauses" part of the UI and "resumes" it once everything is ready, > without manual `isLoading`, `if (!data)`, and so on. --- ### In short: | Capability | Before React 18 | React 18+ | |---|---|---| | Waiting for async components | No | Yes | | SSR + Streaming Suspense | No | Yes | | Integration with React Query / SWR | No | Yes | | Data Fetching in Server Components | No | Yes | | Fallback UI without manual `isLoading` | No | Yes |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.