Promises

Unhandled Rejected Promises

What happens when a Promise sent from server to client is rejected? This example shows a promise that is rejected on the server, and how the client handles that rejection.

page.tsx
import { LoadingMessage } from '@/components/LoadingMessage'

import { Suspense } from 'react'
import Table from '@/components/examples/table'

async function getData(delay: number) {
return new Promise((resolve, reject) => setTimeout(() => {
//SUPER IMPORTANT: You must provide an Error to reject() or next.js will crash
//see https://github.com/vercel/next.js/issues/67863
reject(new Error('Data load failed'))
}, delay))
}

export default function RejectedPromisePage() {
return (
<>
<p>This page was rendered on the server.</p>
<p>
It wants to show a data table below, but the promise is rejected. The
table component is wrapped in a Suspense component, which will show a
loading message until the data is ready. In this case, the data will
never be ready, so the loading message will stay on the screen
</p>

<Suspense fallback={<LoadingMessage />}>
<Table dataPromise={getData(1000)} />
</Suspense>
</>
)
}

What happens here? The getData function returns a Promise that is rejected after a delay. There's no error handling in this example, so what happens is that we'll see an error message in our server logs, and the client will show the loading message indefinitely.

This is not a great UX, so it is better to handle the rejected promise with an ErrorBoundary.

Live Demo

This example can't be easily inlined as it demonstrates how a full-page feels to the end user. Here it is inside an iframe, and there's a looping video below too.

This is an iframe pointing to /examples/promises/rejected

Video Preview

In case the iframe doesn't work for some reason, this is a looping video of what you would see. Click the video to open the full page example in a new tab.

Similar Examples

Previous
Rendering Components