📖 Scenario: You are building a simple blog page using Remix. The page should show a list of blog post titles immediately, while the full content of each post loads separately and streams in as it becomes available.
🎯 Goal: Create a Remix route that uses deferred data with streaming to display blog post titles instantly and load their content asynchronously.
📋 What You'll Learn
Create a loader function that returns deferred data with a list of posts and their content promises
Add a config variable to set a delay time for simulating content loading
Use defer and Await components to stream post content
Complete the component to render titles immediately and content as it streams
💡 Why This Matters
🌍 Real World
Streaming deferred data is useful for improving user experience by showing important content immediately while loading heavier data in the background.
💼 Career
Understanding deferred data and streaming in Remix is valuable for building fast, user-friendly web applications that handle asynchronous data efficiently.
Progress0 / 4 steps
1
Create initial posts data
Create a constant called posts that is an array of two objects. Each object should have id and title properties with these exact values: { id: 1, title: 'First Post' } and { id: 2, title: 'Second Post' }.
Remix
Hint
Use const posts = [ ... ] with two objects inside.
2
Add delay configuration
Create a constant called delayMs and set it to 2000 to simulate a 2-second delay for loading post content.
Remix
Hint
Use const delayMs = 2000; to set the delay.
3
Create loader with deferred content promises
Write an async function called loader that returns defer with an object containing posts and contents. The contents property should be an object where each post's id maps to a promise that resolves after delayMs milliseconds with the string 'Content for post ' + id. Use new Promise and setTimeout to simulate the delay.
Remix
Hint
Use defer to return posts and contents promises that resolve after delayMs.
4
Render posts with streaming content
Create a default exported React component called Posts. Use useLoaderData to get posts and contents. Render a list of post titles immediately. For each post, use the Await component inside a Suspense with fallback <em>Loading...</em> to display the streamed content from contents[post.id].
Remix
Hint
Use useLoaderData to get posts and contents, then map posts rendering titles and streaming content inside Suspense and Await.
Practice
(1/5)
1. What is the main purpose of using defer() in Remix loaders?
easy
A. To cache data permanently on the server
B. To block rendering until all data is loaded
C. To load some data parts asynchronously and improve page speed
D. To preload all images before rendering
Solution
Step 1: Understand what defer() does in Remix loaders
defer() 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 of defer().
Final Answer:
To load some data parts asynchronously and improve page speed -> Option C
Quick Check:
defer() = async partial loading [OK]
Hint: Remember defer() means 'delay some data' to speed up page [OK]
Common Mistakes:
Thinking defer() blocks rendering until all data loads
Confusing defer() with caching or preloading images
Assuming defer() loads everything synchronously
2. Which of the following is the correct way to use defer() in a Remix loader?
easy
A. return defer({ user: fetchUser })
B. return defer(fetchUser())
C. return deferUser(fetchUser())
D. return defer({ user: fetchUser() })
Solution
Step 1: Check the syntax of defer() usage
defer() expects an object with keys mapping to promises or data. So defer({ 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 function deferUser. return defer({ user: fetchUser }) passes the function fetchUser without calling it, so it passes a function, not a promise.
Final Answer:
return defer({ user: fetchUser() }) -> Option D
Quick Check:
defer() needs an object with promises [OK]
Hint: defer() always wraps an object with promises [OK]
Common Mistakes:
Passing a promise directly instead of inside an object
A. The user data immediately, and a loading message for posts
B. Nothing until both user and posts load
C. Only the posts list immediately
D. An error because Suspense is missing for user
Solution
Step 1: Understand deferred data loading
The loader defers both user and posts. The component uses Suspense and Await only 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 A
Quick Check:
User shows immediately; posts show fallback [OK]
Hint: Suspense controls what shows loading fallback [OK]
Common Mistakes:
Assuming all deferred data waits for Suspense
Thinking nothing shows until all data loads
Confusing which data is wrapped in Await
4. This loader code causes an error:
export async function loader() {
const user = fetchUser();
return defer(user);
}
What is the problem and how to fix it?
medium
A. defer() expects an object; fix by returning defer({ user })
B. fetchUser() must be awaited before defer()
C. defer() cannot be used with async functions
D. No problem; code works fine
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 A
Quick Check:
defer() needs an object argument [OK]
Hint: Always pass an object to defer(), never a single promise [OK]
Common Mistakes:
Passing a single promise directly to defer()
Awaiting fetchUser() before defer() unnecessarily
Thinking defer() can't be used with async functions
5. You want to show a user profile page where the user info loads immediately, but the user's posts load later with a loading message. Which is the best way to implement this using deferred data and streaming in Remix?
hard
A. Await both fetchUser() and fetchPosts() in loader, then render all data together
B. Use defer({ user: fetchUser(), posts: fetchPosts() }) in loader, render user directly, wrap posts in Suspense and Await
C. Use defer(fetchUser()) and defer(fetchPosts()) separately in loader
D. Render user and posts without Suspense or Await, rely on browser streaming
Solution
Step 1: Understand deferred data usage for partial loading
Using defer() 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 in Suspense and Await to 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 B
Quick Check:
defer + Suspense/Await for partial loading [OK]
Hint: Wrap only slow data in Suspense/Await, defer all in loader [OK]
Common Mistakes:
Awaiting all data in loader, losing streaming benefits
Using defer() incorrectly with separate calls
Not using Suspense/Await for deferred parts
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.