- Published on
Nested Suspense
How React Nested Suspense Creates Progressive Loading Experiences
React Nested Suspense enables fine-grained loading control by creating multiple independent Suspense boundaries that resolve at different speeds. Page sections load progressively as their data becomes available while maintaining smooth user experiences.
TL;DR
- Nest
<Suspense>
boundaries for independent loading control- Inner boundaries resolve independently without blocking siblings
- Enables progressive content rendering as data becomes available
- Perfect for dashboards, feeds, and multi-section applications
const result = process(data)
The All-or-Nothing Loading Problem
Your React app uses single Suspense boundaries that wait for all data before showing any content, creating frustrating experiences where fast-loading sections are blocked by slow API calls.
// Problem: Single boundary blocks everything
function simulateSingleBoundary() {
const components = [
{ name: 'Profile', time: 100 },
{ name: 'Feed', time: 300 },
{ name: 'Messages', time: 2500 },
]
const maxTime = Math.max(...components.map((c) => c.time))
console.log('All components wait for slowest')
console.log(`Total wait time: ${maxTime}ms`)
console.log('Fast sections blocked')
return maxTime
}
const waitTime = simulateSingleBoundary()
console.log('Poor UX:', waitTime + 'ms wait')
Nested Suspense enables progressive loading where sections appear as they're ready:
// Solution: Nested boundaries load independently
function simulateNestedBoundaries() {
const components = [
{ name: 'Profile', time: 100 },
{ name: 'Feed', time: 300 },
{ name: 'Messages', time: 2500 },
]
components.forEach((comp) => {
setTimeout(() => {
console.log(`${comp.name} ready at ${comp.time}ms`)
}, comp.time)
})
console.log('Progressive loading enabled')
console.log('Fast content appears immediately')
console.log('Slow sections dont block others')
}
Best Practises
Use nested Suspense when:
- ✅ Page sections have different loading speeds (fast vs slow APIs)
- ✅ Progressive content rendering improves perceived performance
- ✅ Independent data sources shouldn't block each other
- ✅ Users benefit from seeing partial content while other sections load
Avoid when:
- 🚩 All data loads at similar speeds (no progressive benefit)
- 🚩 Content has tight interdependencies requiring coordinated loading
- 🚩 Too many boundaries create visual noise and layout jumping
- 🚩 Single loading state provides clearer user experience
System Design Trade-offs
Aspect | Nested Suspense | Single Suspense Boundary |
---|---|---|
Perceived Performance | Excellent - progressive content | Poor - all-or-nothing loading |
User Engagement | High - immediate partial functionality | Low - long waits with no feedback |
Loading Coordination | Independent - sections don't block | Coupled - slowest component blocks all |
Visual Polish | Smooth - content flows in naturally | Jarring - sudden full page appearance |
Flexibility | High - granular loading control | Low - coarse-grained boundaries |
Complexity | Medium - multiple boundaries to manage | Low - single loading state |
More Code Examples
❌ Single boundary
// Single Suspense: Everything waits for slowest
function SimulateSingleSuspense() {
const dashboard = {
header: { data: null, time: 120 },
main: { data: null, time: 200 },
sidebar: { data: null, time: 180 },
analytics: { data: null, time: 4000 },
}
console.log('Loading dashboard with single boundary...')
const startTime = Date.now()
// All sections wait for slowest
const maxTime = Math.max(...Object.values(dashboard).map((section) => section.time))
console.log(`All sections wait ${maxTime}ms`)
console.log('Fast header blocked by slow analytics')
setTimeout(() => {
Object.keys(dashboard).forEach((key) => {
dashboard[key].data = `${key} loaded`
console.log(`${key}: ready but waited ${maxTime}ms`)
})
console.log('Everything appears at once')
console.log('Poor perceived performance')
}, maxTime)
return dashboard
}
const dashboard = SimulateSingleSuspense()
console.log('Initial state:', Object.keys(dashboard).length)
// Output shows blocking behavior
✅ Nested boundaries
// Nested Suspense: Progressive independent loading
function SimulateNestedSuspense() {
const sections = [
{ name: 'header', time: 120 },
{ name: 'main', time: 200 },
{ name: 'sidebar', time: 180 },
{ name: 'analytics', time: 4000 },
]
console.log('Loading with nested boundaries...')
const loaded = []
sections.forEach((section) => {
console.log(`${section.name} starts loading...`)
setTimeout(() => {
loaded.push(section.name)
console.log(`${section.name} ready at ${section.time}ms`)
console.log(`Loaded so far: ${loaded.join(', ')}`)
if (section.name === 'header') {
console.log('Users can see header immediately')
}
if (section.name === 'main') {
console.log('Main content visible, app usable')
}
if (section.name === 'analytics') {
console.log('Analytics loads last, no blocking')
}
}, section.time)
})
console.log('Progressive loading started')
console.log('Each section loads independently')
console.log('Better user experience')
return sections
}
const sections = SimulateNestedSuspense()
console.log('Sections:', sections.length)
// Output shows progressive loading
Technical Trivia
The Shopify Admin Loading Crisis of 2021: Shopify's admin dashboard suffered from terrible loading where store owners waited 8+ seconds to see any content while the entire dashboard loaded as one unit. Critical business metrics and order data were inaccessible during peak sales periods.
Why single Suspense boundaries failed: All admin sections were wrapped in one Suspense boundary, meaning slow analytics blocked immediate content like recent orders and customer messages. Store owners couldn't access urgent order information while waiting for complex analytics to load.
Nested Suspense revolutionized the admin experience: Implementing granular Suspense boundaries allowed the Shopify admin to load progressively. Store owners now see order summaries instantly while analytics load in the background, improving merchant satisfaction by 85% and reducing support tickets dramatically.
Master Nested Suspense: Implementation Strategy
Create Suspense boundaries around independent data sources with different loading speeds to enable progressive loading. Design meaningful skeleton components that match final content layout and group related content when coordination benefits the user.