Remix Deferred Loading — Streaming and Progressive Rendering
In this tutorial, you will learn about Remix Deferred Loading. We cover key concepts, practical examples, and best practices to help you master this topic.
Learn Remix deferred loading: stream critical data first, defer non-critical data, and progressively render content with Suspense for faster initial page loads.
In this lesson, you'll use defer() to send critical data immediately and stream non-critical data, rendering it as it arrives.
What You'll Learn
How to use defer() for streaming responses, render deferred data with <Await>, prioritize critical content, and handle loading states.
Why It Matters
Deferred loading improves perceived performance by rendering the page with essential data first and streaming less important data as it becomes available.
Real-World Use
DodaZIP's dashboard loads user profile and metrics immediately while streaming audit logs and recommendations that are less time-sensitive.
flowchart LR
A[Loader] --> B[defer()]
B --> C[Critical: Instant]
B --> D[Deferred: Stream]
C --> E[Render Immediately]
D --> F[Render When Ready]
style B fill:#121212,color:#fff
Using defer()
import { defer } from "@remix-run/node";
import { Await, useLoaderData } from "@remix-run/react";
import { Suspense } from "react";
export const loader = async () => {
// Critical data: fetched immediately
const user = await getUser();
// Non-critical data: deferred
const recommendations = getRecommendations(user.id); // Promise
return defer({
user,
recommendations, // This is a promise
});
};
export default function Dashboard() {
const { user, recommendations } = useLoaderData<typeof loader>();
return (
<div>
<h1>Welcome, {user.name}</h1>
<p>This renders immediately.</p>
<Suspense fallback={<p>Loading recommendations...</p>}>
<Await resolve={recommendations}>
{(recs) => (
<ul>
{recs.map(rec => <li key={rec.id}>{rec.title}</li>)}
</ul>
)}
</Await>
</Suspense>
</div>
);
}
Multiple Deferred Values
export const loader = async () => {
return defer({
user: getUser(),
notifications: getNotifications(),
suggestions: getSuggestions(),
metrics: getMetrics(), // This one might be slow
});
};
export default function App() {
const data = useLoaderData<typeof loader>();
return (
<div>
<Suspense fallback="Loading user...">
<Await resolve={data.user}>{(user) => <UserProfile user={user} />}</Await>
</Suspense>
<Suspense fallback="Loading notifications...">
<Await resolve={data.notifications}>{(n) => <NotificationBadge count={n.length} />}</Await>
</Suspense>
<Suspense fallback="Loading suggestions...">
<Await resolve={data.suggestions}>{(s) => <SuggestionsList items={s} />}</Await>
</Suspense>
</div>
);
}
Error Handling with Await
Handle errors from deferred promises:
<Suspense fallback={<p>Loading recommendations...</p>}>
<Await
resolve={recommendations}
errorElement={<p>Failed to load recommendations</p>}
>
{(recs) => <RecommendationsList items={recs} />}
</Await>
</Suspense>
Streaming with HTTP/2
Deferred loading uses Remix's streaming response. The initial chunk contains the critical HTML. Subsequent chunks stream as deferred promises resolve.
Common Mistakes
- Deferring critical data: Only defer non-essential content. Data the user sees above the fold should be instant.
- Not wrapping in
<Suspense>:<Await>must be wrapped in<Suspense>. Without it, the component throws during rendering. - Forgetting error handling: Deferred promises can reject. Always provide an
errorElementor error boundary. - Over-deferring: Each deferred value adds streaming overhead. Defer only 2-3 large, non-critical data sources.
- Not testing on slow connections: Verify the streaming experience under throttled network conditions.
Practice Questions
What does
defer()return? Answer: A response that streams critical data immediately and defers promises for non-critical data.How do you render deferred data in a component? Answer: Use
<Await resolve={promise}>wrapped in<Suspense fallback={...}>.What happens if a deferred promise rejects? Answer: The
<Await>component renders theerrorElementor throws to the nearest error boundary.When should you NOT use deferred loading? Answer: For critical above-the-fold content or when the deferred data is needed for the initial render.
Challenge
Build a profile page that loads user info immediately, defers the friends list (simulate 2s delay), and defers the activity feed (simulate 4s delay). Each section should show its own loading state.
Mini Project
Create a project dashboard with three sections: critical metrics (instant), project list (deferred, 1s), and team activity feed (deferred, 3s). Each section streams in as it resolves.
FAQ
What's Next
Learn about Remix Resource Prefetching for speculative loading of resources before navigation.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro