javascript 52 lines · 8 steps

Streaming search results in a Next.js Server Component

A server-rendered search page that streams results independently with Suspense while keeping the input instantly interactive.

Explained by highlit
1import { Suspense } from 'react';
2import { searchProducts } from '@/lib/products';
3import SearchInput from './search-input';
4 
5export const dynamic = 'force-dynamic';
6 
7export default async function ProductSearchPage({ searchParams }) {
8 const { q = '' } = await searchParams;
9 
10 return (
11 <section className="mx-auto max-w-2xl p-6">
12 <h1 className="mb-4 text-xl font-semibold">Search products</h1>
13 <SearchInput defaultValue={q} />
14 <Suspense key={q} fallback={<ResultsSkeleton />}>
15 <SearchResults query={q} />
16 </Suspense>
17 </section>
18 );
19}
20 
21async function SearchResults({ query }) {
22 const term = query.trim();
23 if (!term) {
24 return <p className="mt-6 text-sm text-gray-500">Start typing to search.</p>;
25 }
26 
27 const products = await searchProducts(term);
28 if (products.length === 0) {
29 return <p className="mt-6 text-sm text-gray-500">No matches for “{term}”.</p>;
30 }
31 
32 return (
33 <ul className="mt-6 divide-y">
34 {products.map((product) => (
35 <li key={product.id} className="flex justify-between py-3">
36 <span>{product.name}</span>
37 <span className="text-gray-500">${product.price.toFixed(2)}</span>
38 </li>
39 ))}
40 </ul>
41 );
42}
43 
44function ResultsSkeleton() {
45 return (
46 <ul className="mt-6 animate-pulse space-y-3">
47 {Array.from({ length: 5 }).map((_, i) => (
48 <li key={i} className="h-6 rounded bg-gray-200" />
49 ))}
50 </ul>
51 );
52}
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1Wrapping a slow async component in Suspense lets the page shell render immediately while data streams in behind a fallback.
  2. 2Keying Suspense on the query forces a fresh fallback whenever the search term changes, avoiding stale results.
  3. 3Server Components can await data directly, keeping fetching logic on the server and out of the client bundle.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Streaming search results in a Next.js Server Component — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code