diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
index 0a453e0c07865..cc4f0b5331b72 100644
--- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
@@ -1309,4 +1309,62 @@ describe('ReactFlightDOMBrowser', () => {
'The render was aborted by the server without a reason.',
]);
});
+
+ // @gate enablePostpone
+ it('postpones when abort passes a postpone signal', async () => {
+ const infinitePromise = new Promise(() => {});
+ function Server() {
+ return infinitePromise;
+ }
+
+ let postponed = null;
+ let error = null;
+
+ const controller = new AbortController();
+ const stream = ReactServerDOMServer.renderToReadableStream(
+
+
+ ,
+ null,
+ {
+ onError(x) {
+ error = x;
+ },
+ onPostpone(reason) {
+ postponed = reason;
+ },
+ signal: controller.signal,
+ },
+ );
+
+ try {
+ React.unstable_postpone('testing postpone');
+ } catch (reason) {
+ controller.abort(reason);
+ }
+
+ const response = ReactServerDOMClient.createFromReadableStream(stream);
+
+ function Client() {
+ return use(response);
+ }
+
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await act(async () => {
+ root.render(
+
+ Shell:
+
,
+ );
+ });
+ // We should have reserved the shell already. Which means that the Server
+ // Component should've been a lazy component.
+ expect(container.innerHTML).toContain('Shell:');
+ expect(container.innerHTML).toContain('Loading...');
+ expect(container.innerHTML).not.toContain('Not shown');
+
+ expect(postponed).toBe('testing postpone');
+ expect(error).toBe(null);
+ });
});
diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js
index 5da20fcddae45..479a4a746d5e2 100644
--- a/packages/react-server/src/ReactFlightServer.js
+++ b/packages/react-server/src/ReactFlightServer.js
@@ -1790,15 +1790,27 @@ export function abort(request: Request, reason: mixed): void {
if (abortableTasks.size > 0) {
// We have tasks to abort. We'll emit one error row and then emit a reference
// to that row from every row that's still remaining.
- const error =
- reason === undefined
- ? new Error('The render was aborted by the server without a reason.')
- : reason;
-
- const digest = logRecoverableError(request, error);
request.pendingChunks++;
const errorId = request.nextChunkId++;
- emitErrorChunk(request, errorId, digest, error);
+ if (
+ enablePostpone &&
+ typeof reason === 'object' &&
+ reason !== null &&
+ (reason: any).$$typeof === REACT_POSTPONE_TYPE
+ ) {
+ const postponeInstance: Postpone = (reason: any);
+ logPostpone(request, postponeInstance.message);
+ emitPostponeChunk(request, errorId, postponeInstance);
+ } else {
+ const error =
+ reason === undefined
+ ? new Error(
+ 'The render was aborted by the server without a reason.',
+ )
+ : reason;
+ const digest = logRecoverableError(request, error);
+ emitErrorChunk(request, errorId, digest, error);
+ }
abortableTasks.forEach(task => abortTask(task, request, errorId));
abortableTasks.clear();
}