Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Suspense boundaries के साथ fallback rendering, code splitting और streaming SSR।
Suspense एक ऐसा mechanism है जो components को render होने से पहले कुछ 'प्रतीक्षा' करने देता है। जब कोई component तैयार नहीं होता (lazy import load नहीं हुआ, या data fetch resolve नहीं हुआ), तो वह एक Promise throw करता है। React इस thrown Promise को catch करता है, tree में सबसे नज़दीकी Suspense boundary ढूंढता है, उसका fallback UI दिखाता है, और Promise resolve होने पर subtree को automatically re-render करता है।
React.lazy(() => import('./MyComponent')) एक dynamic import को एक special wrapper में लपेटता है। पहली बार React इसे render करने की कोशिश करता है, import() अभी resolve नहीं हुआ होता, इसलिए lazy wrapper एक Promise throw करता है।
Render phase के दौरान, React thrown Promises को catch करता है (thrown Errors नहीं — वे Error Boundaries के पास जाते हैं)। यह fiber tree में ऊपर traverse करके सबसे नज़दीकी <Suspense> ancestor ढूंढता है और suspended subtree की बजाय उसका fallback prop render करता है।
React thrown Promise पर एक .then() handler जोड़ता है। जब Promise resolve होता है (module load हो जाता है, या data आ जाता है), React suspended subtree को scratch से re-render करता है, इस बार resolved value का उपयोग करके।
Concurrent mode में, React एक suspended subtree को DOM में commit किए बिना 'try' कर सकता है। यदि यह suspend होता है, React पुराना content दिखाता रहता है (fallback पर flash नहीं) जब तक Promise resolve न हो, फिर नए content को atomically swap करता है।
Next.js/React 18 streaming के साथ, server तुरंत Suspense fallbacks के साथ HTML shell भेजता है। जैसे-जैसे server पर प्रत्येक async data fetch resolve होता है, React browser को filled-in HTML chunk stream करता है, fallback को inline replace करते हुए।
एक lazily-loaded component बनाने के लिए dynamic import को wrap करता है। पहले render पर Promise throw करता है। Resolution पर, React loaded module के साथ re-render करता है।
<Suspense fallback='{'<Spinner/>'}'> किसी भी descendant से thrown Promises catch करता है। प्रतीक्षा के दौरान fallback दिखाता है, तैयार होने पर children reveal करता है।
वह mechanism जिसके द्वारा components signal करते हैं कि वे तैयार नहीं हैं। कोई भी component (केवल lazy नहीं) Suspense trigger करने के लिए Promise throw कर सकता है — इसी तरह React Query जैसी data-fetching libraries integrate होती हैं।
Server HTML को progressively भेजता है। Shell तुरंत fallbacks के साथ render होता है। जैसे-जैसे async काम पूरा होता है, React filled content chunks browser को stream करता है।
कई Suspense boundaries के reveal order को coordinate करता है। 'together' (एक साथ reveal) या 'forwards'/'backwards' (क्रम में reveal) enforce कर सकता है।
1// Code splitting with React.lazy2const HeavyChart = React.lazy(() => import('./HeavyChart'));34function App() {5 return (6 <Suspense fallback={<Spinner />}>7 <HeavyChart /> {/* loads the JS bundle on demand */}8 </Suspense>9 );10}1112// Data fetching (with a Suspense-compatible library)13// The library throws a Promise internally when data isn't ready14function UserProfile({ userId }) {15 // use() hook (React 19) or library wrapper throws if not ready16 const user = use(fetchUser(userId));17 return <h1>{user.name}</h1>;18}1920<Suspense fallback={<ProfileSkeleton />}>21 <UserProfile userId={1} />22</Suspense>
Suspense तीन पहले से अलग समस्याओं — code splitting, loading states और server streaming — को एक declarative model के तहत एकजुट करता है। हर component में isLoading flags track करने की बजाय, आप एक boundary और fallback declare करते हैं, और React orchestration संभालता है। इससे data-fetching code काफी सरल होता है और loading state management bugs समाप्त होती हैं।
Your SPA ships a 2MB JavaScript bundle. The landing page only needs 200KB of it — the rest is for the admin panel, chart library, and markdown editor that 80% of users never visit.
Without code splitting, the browser downloads, parses, and executes 2MB of JavaScript before showing anything. On mobile (3G network + slower CPU), this means 8+ seconds before the page is interactive — most users bounce.
Use `React.lazy` + `Suspense` at route boundaries: `const AdminPanel = React.lazy(() => import('./AdminPanel'))`. The initial bundle drops to 200KB. When a user navigates to /admin, the AdminPanel chunk loads on demand. The Suspense fallback shows a skeleton while the chunk downloads.
Takeaway: Code splitting at route boundaries is the highest-impact optimization for initial load time. Wrap lazy-loaded routes in Suspense with skeleton fallbacks. Tools like Next.js do this automatically for page components, but you should also lazy-load heavy feature components within pages.
A dashboard page loads 3 things: sidebar navigation (fast), main content (medium), and analytics charts (slow). With a single Suspense boundary, the entire page shows a spinner until the slowest component (charts) finishes loading.
One Suspense boundary at the page level means the entire page is either 'loading' or 'ready'. The sidebar (50ms) and main content (200ms) are ready quickly, but the user stares at a full-page spinner for 2 seconds waiting for the charts.
Nest multiple Suspense boundaries: one around the sidebar (shows instantly), one around the main content (shows after 200ms), one around the charts (shows after 2s). Each section renders independently as its data/code becomes available. The page progressively reveals content instead of all-or-nothing.
Takeaway: Use nested Suspense boundaries to control loading granularity. The boundary placement determines the loading UX: coarse boundaries (page-level) show everything at once, fine boundaries (component-level) enable progressive, streaming-style loading.