Deferred Data and Suspense Streaming with Await0%

Deferred Data and Suspense Streaming with Await

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Deferred Data and Suspense Streaming with Await

1. The Slow Loader Bottleneck

While Data Loaders eliminate render waterfalls, they introduce a potential drawback:

A route will NOT render until ALL data inside its loader function has finished resolving.

Consider a course page:

  • Fast Data (Critical): Course Title, Instructor, Description (Resolves in 80ms).
  • Slow Data (Non-Critical): 500 Student Reviews & Discussion Threads (Resolves in 1800ms).

If you await both queries in the loader:

JavaScript
// ❌ BAD: Blocks the ENTIRE page transition for 1.8 seconds!
export async function loader({ params }) {
const course = await fetchCourse(params.id); // 80ms
const reviews = await fetchReviews(params.id); // 1800ms (Slow!)
return { course, reviews };
}

The user clicks a course link and sits waiting for 1.8 seconds while the screen does nothing!

2. The Solution: Deferred Data with defer

React Router provides defer() (or streaming promises in modern versions).

You await the critical fast data, but do NOT await the slow data. You pass the unresolved Promise directly into the response:

JavaScript
// src/pages/CourseDetail.jsx
import { defer } from 'react-router-dom';
 
export function courseDetailLoader({ params }) {
// 1. Await critical fast data (Page will transition in 80ms!)
const coursePromise = fetchCourse(params.id);
 
// 2. Do NOT await slow data!
const reviewsPromise = fetchReviews(params.id);
 
return defer({
course: await coursePromise, // Resolved synchronously
reviews: reviewsPromise // Unresolved Promise streamed to client!
});
}

3. Rendering Deferred Data with <Suspense> and <Await>

In your component:

  • The critical data renders instantly.
  • The slow data is wrapped inside React's <Suspense> and React Router's <Await> component, showing a fallback skeleton while the promise resolves in the background!
JSX
import React, { Suspense } from 'react';
import { useLoaderData, Await } from 'react-router-dom';
 
export default function CourseDetail() {
const { course, reviews } = useLoaderData();
 
return (
<div className="course-page">
{/* 1. Fast data renders immediately! Zero delay! */}
<header>
<h1>{course.title}</h1>
<p>{course.description}</p>
</header>
 
{/* 2. Slow data streams in asynchronously without blocking page transition */}
<section className="reviews-section">
<h3>Student Discussion & Feedback</h3>
 
<Suspense fallback={<div className="skeleton-loader">Loading 500 reviews...</div>}>
<Await
resolve={reviews}
errorElement={<p className="text-red">Failed to load reviews.</p>}
>
{(resolvedReviews) => (
<ul className="reviews-list">
{resolvedReviews.map(r => (
<li key={r.id}>
<strong>{r.author}:</strong> {r.comment}
</li>
))}
</ul>
)}
</Await>
</Suspense>
</section>
</div>
);
}

4. Why This Architecture Delivers World-Class UX

  1. 1
    Perceived Performance: The page navigates in 80ms, delivering instant feedback to the user.
  2. 2
    Progressive Rendering: Fast content is readable immediately while secondary sections stream into place.
  3. 3
    Resilient Error Isolation: If the slow reviews API fails, the <Await errorElement={...}> displays a localized error without breaking the main course view!

Interactive Quiz

Practice Quiz

Question 1
What problem does deferring data resolve in React Router?
Question 2
What two components are used together in JSX to render deferred promises?
Question 3
What should you pass to resolve={...} on the <Await> component?
Question 4
If the deferred promise rejects with an error, what component handles the error UI?
Question 5
In the loader, which data should be awaited and which data should be deferred?
Next Lesson

Protected Routes and Role-Based Access Guards

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext