A Client Component cannot be async, and a useEffect fetch reintroduces everything the App Router removed: a round trip that starts after hydration, an empty first paint, and a credential you cannot use because the request leaves the browser. Fetch on the client only when the data belongs there — a value that changes while the page is open, or depends on interaction.
The first option keeps the request on the server. Call the loader without await, pass the promise across the boundary (promises are serializable, Serializing Props), and unwrap it with React 7,897 's use:
export default function Live() { // Server Component
const books = listBooks(3); // no await: the promise is the prop
return <main><h1>Books</h1><Suspense fallback={<p>Loading...</p>}>
<BookList books={books} /></Suspense></main>;
}
"use client"; // app/live/book-list.js
export default function BookList({ books }) {
const all = use(books); // suspends until it resolves
const [q, setQ] = useState("");
const shown = all.filter((b) => b.title.toLowerCase().includes(q.toLowerCase()));
return <><input value={q} onChange={(e) => setQ(e.target.value)} />
<ul>{shown.map((b) => <li key={b.id}>{b.title}</li>)}</ul></>;
}The response streams, and the filter then runs in the browser with no further requests:
$ curl -s localhost:3310/live | grep -o "<li>[^<]*\|Loading\.\.\." Loading... <li>The Dispossessed <li>A Wizard of Earthsea <li>Solaris Chromium 153, after typing "the" into the filter: Books | The Dispossessed
The second option is a real client library, for data that must refresh without a navigation. SWR 2.5.1 639 (from the Next.js 10,514 team, npm 2,036 install swr) and TanStack Query 79,069 add caching, revalidation on focus, polling and retry: useSWR("/api/stats", fetcher, { refreshInterval: 2000 }) returns data, error and isLoading. The cost shows in the HTML — curl 3,008 on that page returns <p>Loading...</p> and nothing else, so a crawler and a slow phone both see the spinner. Point such a component at your own Route Handler (Route Handlers and the Proxy), never at the upstream API, so the credential stays on the server.