diff --git a/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js b/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js
index 1a2f466263b1d..05f54acb56df3 100644
--- a/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js
+++ b/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js
@@ -23,6 +23,7 @@ import {
enableFilterEmptyStringAttributesDOM,
enableCustomElementPropertySupport,
enableFloat,
+ enableFizzExternalRuntime,
} from 'shared/ReactFeatureFlags';
import type {
@@ -157,6 +158,7 @@ export function createResponseState(
bootstrapScriptContent: string | void,
bootstrapScripts: $ReadOnlyArray | void,
bootstrapModules: $ReadOnlyArray | void,
+ externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
): ResponseState {
const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix;
const inlineScriptWithNonce =
@@ -173,6 +175,29 @@ export function createResponseState(
endInlineScript,
);
}
+ if (enableFizzExternalRuntime) {
+ if (externalRuntimeConfig !== undefined) {
+ const src =
+ typeof externalRuntimeConfig === 'string'
+ ? externalRuntimeConfig
+ : externalRuntimeConfig.src;
+ const integrity =
+ typeof externalRuntimeConfig === 'string'
+ ? undefined
+ : externalRuntimeConfig.integrity;
+ bootstrapChunks.push(
+ startScriptSrc,
+ stringToChunk(escapeTextForBrowser(src)),
+ );
+ if (integrity) {
+ bootstrapChunks.push(
+ scriptIntegirty,
+ stringToChunk(escapeTextForBrowser(integrity)),
+ );
+ }
+ bootstrapChunks.push(endAsyncScript);
+ }
+ }
if (bootstrapScripts !== undefined) {
for (let i = 0; i < bootstrapScripts.length; i++) {
const scriptConfig = bootstrapScripts[i];
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
index 82d103cfecf89..07a6684b1b809 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
@@ -3546,6 +3546,36 @@ describe('ReactDOMFizzServer', () => {
});
});
+ // @gate enableFizzExternalRuntime
+ it('supports option to load runtime as an external script', async () => {
+ await actIntoEmptyDocument(() => {
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
+
+
+
+ hello world
+
+ ,
+ {
+ unstable_externalRuntimeSrc: 'src-of-external-runtime',
+ },
+ );
+ pipe(writable);
+ });
+
+ expect(getVisibleChildren(document)).toEqual(
+
+
+
+ hello world
+
+ ,
+ );
+ expect(
+ Array.from(document.getElementsByTagName('script')).map(n => n.outerHTML),
+ ).toEqual(['']);
+ });
+
it('#24384: Suspending should halt hydration warnings and not emit any if hydration completes successfully after unsuspending', async () => {
const makeApp = () => {
let resolve, resolved;
diff --git a/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
index 2394f656c53a7..ffe7e07d10732 100644
--- a/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
+++ b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
@@ -34,6 +34,7 @@ type Options = {
progressiveChunkSize?: number,
signal?: AbortSignal,
onError?: (error: mixed) => ?string,
+ unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
};
// TODO: Move to sub-classing ReadableStream.
@@ -86,6 +87,7 @@ function renderToReadableStream(
options ? options.bootstrapScriptContent : undefined,
options ? options.bootstrapScripts : undefined,
options ? options.bootstrapModules : undefined,
+ options ? options.unstable_externalRuntimeSrc : undefined,
),
createRootFormatContext(options ? options.namespaceURI : undefined),
options ? options.progressiveChunkSize : undefined,
diff --git a/packages/react-dom/src/server/ReactDOMFizzServerNode.js b/packages/react-dom/src/server/ReactDOMFizzServerNode.js
index 5280dd50a13a5..ccc930cf8890d 100644
--- a/packages/react-dom/src/server/ReactDOMFizzServerNode.js
+++ b/packages/react-dom/src/server/ReactDOMFizzServerNode.js
@@ -47,6 +47,7 @@ type Options = {
onShellError?: (error: mixed) => void,
onAllReady?: () => void,
onError?: (error: mixed) => ?string,
+ unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
};
type PipeableStream = {
@@ -65,6 +66,7 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) {
options ? options.bootstrapScriptContent : undefined,
options ? options.bootstrapScripts : undefined,
options ? options.bootstrapModules : undefined,
+ options ? options.unstable_externalRuntimeSrc : undefined,
),
createRootFormatContext(options ? options.namespaceURI : undefined),
options ? options.progressiveChunkSize : undefined,
diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js b/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
index 743e807a0594f..7607579db56cf 100644
--- a/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
+++ b/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
@@ -33,6 +33,7 @@ type Options = {
progressiveChunkSize?: number,
signal?: AbortSignal,
onError?: (error: mixed) => ?string,
+ unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
};
type StaticResult = {
@@ -71,6 +72,7 @@ function prerender(
options ? options.bootstrapScriptContent : undefined,
options ? options.bootstrapScripts : undefined,
options ? options.bootstrapModules : undefined,
+ options ? options.unstable_externalRuntimeSrc : undefined,
),
createRootFormatContext(options ? options.namespaceURI : undefined),
options ? options.progressiveChunkSize : undefined,
diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticNode.js b/packages/react-dom/src/server/ReactDOMFizzStaticNode.js
index 74631c3e19f8f..0a8ff59a3f4a5 100644
--- a/packages/react-dom/src/server/ReactDOMFizzStaticNode.js
+++ b/packages/react-dom/src/server/ReactDOMFizzStaticNode.js
@@ -35,6 +35,7 @@ type Options = {
progressiveChunkSize?: number,
signal?: AbortSignal,
onError?: (error: mixed) => ?string,
+ unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
};
type StaticResult = {
@@ -86,6 +87,7 @@ function prerenderToNodeStreams(
options ? options.bootstrapScriptContent : undefined,
options ? options.bootstrapScripts : undefined,
options ? options.bootstrapModules : undefined,
+ options ? options.unstable_externalRuntimeSrc : undefined,
),
createRootFormatContext(options ? options.namespaceURI : undefined),
options ? options.progressiveChunkSize : undefined,
diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js
index f43beacb79d7e..73600f26f066a 100644
--- a/packages/shared/ReactFeatureFlags.js
+++ b/packages/shared/ReactFeatureFlags.js
@@ -125,6 +125,9 @@ export const enableUseMemoCacheHook = __EXPERIMENTAL__;
export const enableUseEventHook = __EXPERIMENTAL__;
+// Test in www before enabling in open source.
+export const enableFizzExternalRuntime = false;
+
// -----------------------------------------------------------------------------
// Chopping Block
//
diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js
index 751377fbb1d33..b241a874da044 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-fb.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js
@@ -86,6 +86,7 @@ export const enableFloat = false;
export const enableHostSingletons = false;
export const useModernStrictMode = false;
+export const enableFizzExternalRuntime = false;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js
index 7dc0ac1e2febc..31bb0838a847d 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-oss.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js
@@ -76,6 +76,7 @@ export const enableFloat = false;
export const enableHostSingletons = false;
export const useModernStrictMode = false;
+export const enableFizzExternalRuntime = false;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index 2d408c9c10f36..5c910a102907f 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -76,6 +76,7 @@ export const enableFloat = false;
export const enableHostSingletons = false;
export const useModernStrictMode = false;
+export const enableFizzExternalRuntime = false;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
index 9ff5908cc4e87..6c0ee8735c287 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
@@ -78,6 +78,7 @@ export const enableFloat = false;
export const enableHostSingletons = false;
export const useModernStrictMode = false;
+export const enableFizzExternalRuntime = false;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js
index f2346a66aa30f..18add772cb481 100644
--- a/packages/shared/forks/ReactFeatureFlags.testing.js
+++ b/packages/shared/forks/ReactFeatureFlags.testing.js
@@ -76,6 +76,7 @@ export const enableFloat = false;
export const enableHostSingletons = false;
export const useModernStrictMode = false;
+export const enableFizzExternalRuntime = false;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js
index 7114d9d21289f..152189a407e17 100644
--- a/packages/shared/forks/ReactFeatureFlags.testing.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js
@@ -77,6 +77,7 @@ export const enableFloat = false;
export const enableHostSingletons = false;
export const useModernStrictMode = false;
+export const enableFizzExternalRuntime = false;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js
index 755d44053c465..fb44dca295e00 100644
--- a/packages/shared/forks/ReactFeatureFlags.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -113,6 +113,7 @@ export const enableUseMutableSource = true;
export const enableCustomElementPropertySupport = __EXPERIMENTAL__;
export const useModernStrictMode = false;
+export const enableFizzExternalRuntime = true;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);