What if your app could show results bit by bit instead of making users wait forever?
Why Streaming long operations in NextJS? - Purpose & Use Cases
Imagine waiting for a website to load a huge report or a long list of data all at once, staring at a blank screen with no updates.
Loading everything before showing anything makes users impatient and unsure if the site is working. It also wastes bandwidth and server resources.
Streaming lets the website send pieces of data as they are ready, so users see progress and can start interacting sooner.
const data = await fetchBigData(); return <Display data={data} />;const stream = fetchBigDataStream(); return <StreamDisplay stream={stream} />;Users get faster feedback and smoother experiences even with heavy or slow operations.
When watching a video online, streaming sends parts of the video bit by bit so playback starts quickly without waiting for the whole file.
Loading all data at once causes delays and frustration.
Streaming sends data in chunks as ready, improving responsiveness.
This makes apps feel faster and more user-friendly.
Practice
Solution
Step 1: Understand streaming purpose
Streaming sends data in chunks so users can see progress during long operations.Step 2: Identify user experience impact
This reduces the perceived wait time by showing partial results early.Final Answer:
Users see data progressively, reducing perceived wait time. -> Option AQuick Check:
Streaming improves user experience = A [OK]
- Thinking streaming caches data automatically
- Confusing streaming with data compression
- Assuming streaming prevents all errors
Solution
Step 1: Recall correct ReadableStream usage
ReadableStream requires a start method with a controller to enqueue data and close the stream.Step 2: Match syntax with Next.js streaming pattern
const stream = new ReadableStream({ start(controller) { controller.enqueue('data'); controller.close(); } }); return new Response(stream); uses controller.enqueue and controller.close correctly and returns a Response wrapping the stream.Final Answer:
const stream = new ReadableStream({ start(controller) { controller.enqueue('data'); controller.close(); } }); return new Response(stream); -> Option AQuick Check:
ReadableStream with controller.enqueue = B [OK]
- Using WritableStream instead of ReadableStream
- Calling non-existent methods like send() or push()
- Not closing the stream after enqueue
export async function GET() {
const stream = new ReadableStream({
async start(controller) {
controller.enqueue(new TextEncoder().encode('Hello '));
await new Promise(r => setTimeout(r, 1000));
controller.enqueue(new TextEncoder().encode('World!'));
controller.close();
}
});
return new Response(stream);
}Solution
Step 1: Analyze streaming behavior
The stream first sends "Hello " immediately, then waits 1 second before sending "World!".Step 2: Understand client reception
The client receives data in two chunks with a delay, then the stream closes properly.Final Answer:
"Hello World!" received in two chunks with 1 second delay between. -> Option BQuick Check:
Streaming sends chunks with delay = C [OK]
- Assuming all data arrives after delay
- Thinking stream never closes
- Confusing async enqueue with syntax error
export async function GET() {
const stream = new ReadableStream({
start(controller) {
controller.enqueue('Data chunk');
// Missing controller.close()
}
});
return new Response(stream);
}Solution
Step 1: Check stream lifecycle
The code enqueues data but does not call controller.close(), so the stream stays open.Step 2: Understand client impact
The client waits forever for the stream to end, causing a hang or timeout.Final Answer:
The stream never closes, causing the client to wait indefinitely. -> Option CQuick Check:
Missing controller.close() = stream hangs [OK]
- Forgetting to close the stream
- Thinking enqueue must be async
- Misunderstanding Response usage
Solution
Step 1: Understand streaming large data
Streaming each item as JSON chunk progressively sends data without waiting for all items.Step 2: Identify correct streaming pattern
Using ReadableStream with async start allows looping over items, enqueueing each chunk, then closing the stream.Step 3: Evaluate other options
A requires multiple requests (inefficient); B sends all data at once (no streaming benefit); C uses wrong WritableStream type.Final Answer:
Use ReadableStream with async start(controller), loop over items, enqueue JSON strings, and close after all. -> Option DQuick Check:
Stream chunks in loop with ReadableStream = D [OK]
- Sending all data at once, losing streaming benefit
- Using WritableStream instead of ReadableStream
- Making multiple fetch calls instead of streaming
Ready to prove it?
Pass the test and this pattern moves unknown
