Next.js Promise Racing

June 30, 2025

357 words

Post contents

While working on my next article, I was reminded of a Next.js demo I posted to X/Twitter some time ago.

This demo showcases how you can use React Server Components (RSCs) to say "Fully resolve a promise on the server unless it takes longer than N seconds. If it does, then show a spinner on the client":

import { Suspense } from "react";// Simulate an async data fetching functionfunction fetchUser() {	return new Promise((resolve) => {		setTimeout(() => {			resolve({ name: "Corbin Crutchley" });		}, 2000);	});}// Race the passed promise against a timeout of 1 secondfunction race(promise) {	return Promise.any([		promise,		new Promise((resolve) => setTimeout(() => resolve(), 1000)),	]);}async function UserDisplay({ promise }) {	const user = await promise;	return <div>{user.name}</div>;}export default async function Page() {	// Start fetching user data	const userPromise = fetchUser();	// If the user data takes longer than 1 second, we will not wait for it	// and instead render a fallback UI.	await race(userPromise);	return (		<Suspense fallback={<div>Loading...</div>}>			<UserDisplay promise={userPromise} />		</Suspense>	);}

This is so cool to me. Notice we didn't need any "use client" API usage; these are all server components! But despite that, we're still using the previously client-only <Suspense> component to handle data loading!

Not only do React's client and server APIs marry in this code sample, but even the ideas behind JSX-over-the-wire are on full display here: We're serializing a promise to send over the wire. Conditionally.

This means that if the promise resolves in time, it will never ship the loading <div> to the client!

Likewise, if it doesn't resolve in time, the promise will "continue" on the client via Next.js' RPC mechanisms. Not only is this a neat party trick, but it comes with performance benefits as well. In the example above, fetchUser takes 2 seconds but the wait time is only 1 second. When the promise is passed to the user in this way, they'll only have to wait 1 addition second from the original wait time to resolve.

Pretty cool, right?

Subscribe to our newsletter!

Subscribe to our newsletter to get updates on new content we create, events we have coming up, and more! We'll make sure not to spam you and provide good insights to the content we have.