Deferred data with streaming lets your app show parts of the page quickly while waiting for slower data. This makes the user experience smoother and faster.
Deferred data with streaming in Remix
import { defer } from '@remix-run/node'; import { useLoaderData, Await } from '@remix-run/react'; import { Suspense } from 'react'; export const loader = async () => { return defer({ fastData: fetchFastData(), slowData: fetchSlowData() }); }; export default function Component() { const { fastData, slowData } = useLoaderData(); return ( <> <div>{fastData}</div> <Suspense fallback={<div>Loading slow data...</div>}> <Await resolve={slowData}> {(data) => <div>{data}</div>} </Await> </Suspense> </> ); }
defer() wraps data to load it asynchronously.
Suspense and Await components handle showing loading states and rendering deferred data.
defer.export const loader = () => {
return defer({
message: Promise.resolve('Hello!'),
delayed: new Promise(resolve => setTimeout(() => resolve('Delayed data'), 2000))
});
};delayed data arrives, then renders it.<Suspense fallback={<div>Loading...</div>}>
<Await resolve={delayed}>
{(data) => <p>{data}</p>}
</Await>
</Suspense>This component shows fast data immediately. It uses Suspense and Await to wait for slow data and show a loading message meanwhile.
import { defer } from '@remix-run/node'; import { useLoaderData, Await } from '@remix-run/react'; import { Suspense } from 'react'; function fetchFastData() { return 'Fast data loaded'; } function fetchSlowData() { return new Promise(resolve => setTimeout(() => resolve('Slow data loaded'), 3000)); } export const loader = () => { return defer({ fastData: fetchFastData(), slowData: fetchSlowData() }); }; export default function DeferredExample() { const { fastData, slowData } = useLoaderData(); return ( <main> <h1>Deferred Data Example</h1> <p>{fastData}</p> <Suspense fallback={<p>Loading slow data...</p>}> <Await resolve={slowData}> {(data) => <p>{data}</p>} </Await> </Suspense> </main> ); }
Always wrap deferred data with defer() in your loader.
Use Suspense with a fallback UI to show loading states for slow data.
Remember that fast data renders immediately, improving user experience.
Deferred data lets you load parts of your page at different speeds.
Use defer() in loaders and Suspense + Await in components.
This improves page speed and user experience by showing content early.
Practice
defer() in Remix loaders?Solution
Step 1: Understand what
defer()does in Remix loadersdefer()allows parts of the data to load asynchronously, so the page can start rendering before all data arrives.Step 2: Compare options with this behavior
To load some data parts asynchronously and improve page speed correctly describes asynchronous partial loading to improve speed. Options A, B, and D describe blocking, caching, or preloading images, which are not the purpose ofdefer().Final Answer:
To load some data parts asynchronously and improve page speed -> Option CQuick Check:
defer() = async partial loading [OK]
- Thinking defer() blocks rendering until all data loads
- Confusing defer() with caching or preloading images
- Assuming defer() loads everything synchronously
defer() in a Remix loader?Solution
Step 1: Check the syntax of defer() usage
defer()expects an object with keys mapping to promises or data. Sodefer({ user: fetchUser() })is correct because it passes an object with a promise.Step 2: Analyze other options
return defer(fetchUser()) passes a promise directly, not an object. return deferUser(fetchUser()) uses a non-existent functiondeferUser. return defer({ user: fetchUser }) passes the functionfetchUserwithout calling it, so it passes a function, not a promise.Final Answer:
return defer({ user: fetchUser() }) -> Option DQuick Check:
defer() needs an object with promises [OK]
- Passing a promise directly instead of inside an object
- Passing a function instead of calling it
- Using non-existent functions like deferUser
export async function loader() {
return defer({
user: fetchUser(),
posts: fetchPosts()
});
}And this component snippet:
<Suspense fallback="<p>Loading posts...</p>">
<Await resolve={data.posts}>
{(posts) => <PostList posts={posts} />}
</Await>
</Suspense>What will the user see first when the page loads?
Solution
Step 1: Understand deferred data loading
The loader defers both user and posts. The component usesSuspenseandAwaitonly for posts, so posts will show a loading fallback until ready.Step 2: Predict what renders first
User data is not wrapped in Suspense here, so it will render immediately if available. Posts will show the fallback message until loaded.Final Answer:
The user data immediately, and a loading message for posts -> Option AQuick Check:
User shows immediately; posts show fallback [OK]
- Assuming all deferred data waits for Suspense
- Thinking nothing shows until all data loads
- Confusing which data is wrapped in Await
export async function loader() {
const user = fetchUser();
return defer(user);
}What is the problem and how to fix it?
Solution
Step 1: Identify defer() argument requirements
defer()requires an object mapping keys to promises or data, not a single promise.Step 2: Fix the code
Wrap the promise in an object:defer({ user })to fix the error.Final Answer:
defer() expects an object; fix by returning defer({ user }) -> Option AQuick Check:
defer() needs an object argument [OK]
- Passing a single promise directly to defer()
- Awaiting fetchUser() before defer() unnecessarily
- Thinking defer() can't be used with async functions
Solution
Step 1: Understand deferred data usage for partial loading
Usingdefer()with an object containing both user and posts lets you load them independently.Step 2: Use Suspense and Await to show loading fallback only for posts
Render user data directly since it loads fast, and wrap posts inSuspenseandAwaitto show a loading message until posts arrive.Final Answer:
Use defer({ user: fetchUser(), posts: fetchPosts() }) in loader, render user directly, wrap posts in Suspense and Await -> Option BQuick Check:
defer + Suspense/Await for partial loading [OK]
- Awaiting all data in loader, losing streaming benefits
- Using defer() incorrectly with separate calls
- Not using Suspense/Await for deferred parts
Ready to prove it?
Pass the test and this pattern moves unknown
