diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js
index fe5532de5c339..ea6a09619edf1 100644
--- a/packages/react-client/src/ReactFlightClient.js
+++ b/packages/react-client/src/ReactFlightClient.js
@@ -556,6 +556,11 @@ export function parseModelString(
throw chunk.reason;
}
}
+ case 'u': {
+ // matches "$undefined"
+ // Special encoding for `undefined` which can't be serialized as JSON otherwise.
+ return undefined;
+ }
default: {
// We assume that anything else is a reference ID.
const id = parseInt(value.substring(1), 16);
diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js
index 0e507876d965b..be92bb2d9eb01 100644
--- a/packages/react-client/src/__tests__/ReactFlight-test.js
+++ b/packages/react-client/src/__tests__/ReactFlight-test.js
@@ -197,6 +197,38 @@ describe('ReactFlight', () => {
expect(ReactNoop).toMatchRenderedOutput(ABC);
});
+ it('can render undefined', async () => {
+ function Undefined() {
+ return undefined;
+ }
+
+ const model = ;
+
+ const transport = ReactNoopFlightServer.render(model);
+
+ await act(async () => {
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
+ });
+
+ expect(ReactNoop).toMatchRenderedOutput(null);
+ });
+
+ it('can render an empty fragment', async () => {
+ function Empty() {
+ return ;
+ }
+
+ const model = ;
+
+ const transport = ReactNoopFlightServer.render(model);
+
+ await act(async () => {
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
+ });
+
+ expect(ReactNoop).toMatchRenderedOutput(null);
+ });
+
it('can render a lazy component as a shared component on the server', async () => {
function SharedComponent({text}) {
return (
diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js
index 2f82e024b67fb..5f7319972d8bf 100644
--- a/packages/react-server/src/ReactFlightServer.js
+++ b/packages/react-server/src/ReactFlightServer.js
@@ -117,6 +117,7 @@ export type ReactClientValue =
| number
| symbol
| null
+ | void
| Iterable
| Array
| ReactClientObject
@@ -546,6 +547,10 @@ function serializeProviderReference(name: string): string {
return '$P' + name;
}
+function serializeUndefined(): string {
+ return '$undefined';
+}
+
function serializeClientReference(
request: Request,
parent:
@@ -1134,14 +1139,14 @@ export function resolveModelToJSON(
return escapeStringValue(value);
}
- if (
- typeof value === 'boolean' ||
- typeof value === 'number' ||
- typeof value === 'undefined'
- ) {
+ if (typeof value === 'boolean' || typeof value === 'number') {
return value;
}
+ if (typeof value === 'undefined') {
+ return serializeUndefined();
+ }
+
if (typeof value === 'function') {
if (isClientReference(value)) {
return serializeClientReference(request, parent, key, (value: any));