@module-federation/bridge-react 0.0.0-next-20250407024707 → 0.0.0-next-20250408024819

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,26 @@
1
1
  # @module-federation/bridge-react
2
2
 
3
- ## 0.0.0-next-20250407024707
3
+ ## 0.0.0-next-20250408024819
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - Updated dependencies [695aced]
8
- - @module-federation/sdk@0.0.0-next-20250407024707
9
- - @module-federation/bridge-shared@0.0.0-next-20250407024707
7
+ - chore: bump next
8
+ - @module-federation/sdk@0.0.0-next-20250408024819
9
+ - @module-federation/bridge-shared@0.0.0-next-20250408024819
10
+
11
+ ## 0.12.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 8b3f9d0: feat(react-bridge): Add native support for React 19 in bridge-react with enhanced createRoot options
16
+
17
+ ### Patch Changes
18
+
19
+ - 8b3f9d0: feat(bridge-react): support react v19 in react compat file.
20
+ - Updated dependencies [64a2bc1]
21
+ - Updated dependencies [c14842f]
22
+ - @module-federation/sdk@0.12.0
23
+ - @module-federation/bridge-shared@0.12.0
10
24
 
11
25
  ## 0.11.3
12
26
 
package/dist/index.cjs.js CHANGED
@@ -1,5 +1,27 @@
1
1
  "use strict";
2
- var _a;
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var _a, _b;
3
25
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
26
  const React = require("react");
5
27
  const context = require("./context-C79iMWYD.cjs");
@@ -146,13 +168,13 @@ const RemoteAppWrapper = React.forwardRef(function(props, ref) {
146
168
  providerInfoRef.current = providerReturn;
147
169
  setInitialized(true);
148
170
  return () => {
149
- var _a2, _b, _c, _d, _e, _f, _g, _h;
171
+ var _a2, _b2, _c, _d, _e, _f, _g, _h;
150
172
  if ((_a2 = providerInfoRef.current) == null ? void 0 : _a2.destroy) {
151
173
  context.LoggerInstance.debug(
152
174
  `createRemoteComponent LazyComponent destroy >>>`,
153
175
  { moduleName, basename, dom: renderDom.current }
154
176
  );
155
- (_d = (_c = (_b = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _b.lifecycle) == null ? void 0 : _c.beforeBridgeDestroy) == null ? void 0 : _d.emit({
177
+ (_d = (_c = (_b2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _b2.lifecycle) == null ? void 0 : _c.beforeBridgeDestroy) == null ? void 0 : _d.emit({
156
178
  moduleName,
157
179
  dom: renderDom.current,
158
180
  basename,
@@ -176,7 +198,7 @@ const RemoteAppWrapper = React.forwardRef(function(props, ref) {
176
198
  };
177
199
  }, [moduleName]);
178
200
  React.useEffect(() => {
179
- var _a2, _b, _c, _d, _e, _f;
201
+ var _a2, _b2, _c, _d, _e, _f;
180
202
  if (!initialized || !providerInfoRef.current) return;
181
203
  let renderProps = {
182
204
  moduleName,
@@ -187,7 +209,7 @@ const RemoteAppWrapper = React.forwardRef(function(props, ref) {
187
209
  ...resProps
188
210
  };
189
211
  renderDom.current = rootRef.current;
190
- const beforeBridgeRenderRes = ((_c = (_b = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b.beforeBridgeRender) == null ? void 0 : _c.emit(renderProps)) || {};
212
+ const beforeBridgeRenderRes = ((_c = (_b2 = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b2.beforeBridgeRender) == null ? void 0 : _c.emit(renderProps)) || {};
191
213
  renderProps = { ...renderProps, ...beforeBridgeRenderRes.extraProps };
192
214
  providerInfoRef.current.render(renderProps);
193
215
  (_f = (_e = (_d = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _d.lifecycle) == null ? void 0 : _e.afterBridgeRender) == null ? void 0 : _f.emit(renderProps);
@@ -322,10 +344,40 @@ function createRemoteComponent(info) {
322
344
  });
323
345
  }
324
346
  const isReact18 = (_a = ReactDOM.version) == null ? void 0 : _a.startsWith("18");
325
- function createRoot(container, options) {
326
- if (isReact18) {
327
- return ReactDOM.createRoot(container, options);
347
+ const isReact19 = (_b = ReactDOM.version) == null ? void 0 : _b.startsWith("19");
348
+ let reactDOMClientPromise = null;
349
+ async function loadReactDOMClient() {
350
+ if (!isReact19) return null;
351
+ if (!reactDOMClientPromise) {
352
+ reactDOMClientPromise = import("react-dom/client");
328
353
  }
354
+ return reactDOMClientPromise;
355
+ }
356
+ function createReact19Root(container, options) {
357
+ loadReactDOMClient();
358
+ return {
359
+ render(children) {
360
+ loadReactDOMClient().then((client) => {
361
+ if (client && client.createRoot) {
362
+ const root = client.createRoot(container, options);
363
+ root.render(children);
364
+ }
365
+ });
366
+ },
367
+ unmount() {
368
+ loadReactDOMClient().then((client) => {
369
+ if (client && client.createRoot) {
370
+ const root = client.createRoot(container, options);
371
+ root.unmount();
372
+ }
373
+ });
374
+ }
375
+ };
376
+ }
377
+ function createReact18Root(container, options) {
378
+ return ReactDOM.createRoot(container, options);
379
+ }
380
+ function createReact16Or17Root(container) {
329
381
  return {
330
382
  render(children) {
331
383
  ReactDOM.render(children, container);
@@ -335,8 +387,18 @@ function createRoot(container, options) {
335
387
  }
336
388
  };
337
389
  }
390
+ function createRoot(container, options) {
391
+ if (isReact19) {
392
+ return createReact19Root(container, options);
393
+ }
394
+ if (isReact18) {
395
+ return createReact18Root(container, options);
396
+ }
397
+ return createReact16Or17Root(container);
398
+ }
338
399
  function createBridgeComponent({
339
400
  createRoot: createRoot$1 = createRoot,
401
+ defaultRootOptions,
340
402
  ...bridgeInfo
341
403
  }) {
342
404
  return () => {
@@ -360,7 +422,7 @@ function createBridgeComponent({
360
422
  };
361
423
  return {
362
424
  async render(info) {
363
- var _a2, _b, _c, _d, _e, _f;
425
+ var _a2, _b2, _c, _d, _e, _f;
364
426
  context.LoggerInstance.debug(`createBridgeComponent render Info`, info);
365
427
  const {
366
428
  moduleName,
@@ -368,9 +430,14 @@ function createBridgeComponent({
368
430
  basename,
369
431
  memoryRoute,
370
432
  fallback,
433
+ rootOptions,
371
434
  ...propsInfo
372
435
  } = info;
373
- const beforeBridgeRenderRes = ((_c = (_b = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b.beforeBridgeRender) == null ? void 0 : _c.emit(info)) || {};
436
+ const mergedRootOptions = {
437
+ ...defaultRootOptions,
438
+ ...rootOptions
439
+ };
440
+ const beforeBridgeRenderRes = ((_c = (_b2 = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b2.beforeBridgeRender) == null ? void 0 : _c.emit(info)) || {};
374
441
  const rootComponentWithErrorBoundary = /* @__PURE__ */ React__namespace.createElement(ErrorBoundary, { FallbackComponent: fallback }, /* @__PURE__ */ React__namespace.createElement(
375
442
  RawComponent,
376
443
  {
@@ -379,7 +446,10 @@ function createBridgeComponent({
379
446
  basename,
380
447
  memoryRoute
381
448
  },
382
- propsInfo: { ...propsInfo, ...beforeBridgeRenderRes == null ? void 0 : beforeBridgeRenderRes.extraProps }
449
+ propsInfo: {
450
+ ...propsInfo,
451
+ ...beforeBridgeRenderRes == null ? void 0 : beforeBridgeRenderRes.extraProps
452
+ }
383
453
  }
384
454
  ));
385
455
  if (bridgeInfo.render) {
@@ -389,7 +459,7 @@ function createBridgeComponent({
389
459
  } else {
390
460
  let root = rootMap.get(dom);
391
461
  if (!root) {
392
- root = createRoot$1(dom);
462
+ root = createRoot$1(dom, mergedRootOptions);
393
463
  rootMap.set(dom, root);
394
464
  }
395
465
  if ("render" in root) {
@@ -399,7 +469,7 @@ function createBridgeComponent({
399
469
  ((_f = (_e = (_d = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _d.lifecycle) == null ? void 0 : _e.afterBridgeRender) == null ? void 0 : _f.emit(info)) || {};
400
470
  },
401
471
  destroy(info) {
402
- var _a2, _b, _c;
472
+ var _a2, _b2, _c;
403
473
  const { dom } = info;
404
474
  context.LoggerInstance.debug(`createBridgeComponent destroy Info`, info);
405
475
  const root = rootMap.get(dom);
@@ -411,7 +481,7 @@ function createBridgeComponent({
411
481
  }
412
482
  rootMap.delete(dom);
413
483
  }
414
- (_c = (_b = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b.afterBridgeDestroy) == null ? void 0 : _c.emit(info);
484
+ (_c = (_b2 = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b2.afterBridgeDestroy) == null ? void 0 : _c.emit(info);
415
485
  }
416
486
  };
417
487
  };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { default as default_2 } from 'react';
2
2
  import * as React_2 from 'react';
3
3
 
4
- export declare function createBridgeComponent<T>({ createRoot, ...bridgeInfo }: ProviderFnParams<T>): () => {
4
+ export declare function createBridgeComponent<T>({ createRoot, defaultRootOptions, ...bridgeInfo }: ProviderFnParams<T>): () => {
5
5
  render(info: RenderParams): Promise<void>;
6
6
  destroy(info: DestroyParams): void;
7
7
  };
@@ -34,6 +34,16 @@ declare interface ProviderFnParams<T> {
34
34
  rootComponent: React_2.ComponentType<T>;
35
35
  render?: (App: React_2.ReactElement, id?: HTMLElement | string) => RootType | Promise<RootType>;
36
36
  createRoot?: (container: Element | DocumentFragment, options?: CreateRootOptions) => Root;
37
+ /**
38
+ * Default options to pass to createRoot for React 18 and 19
39
+ * These options will be used when creating a root unless overridden by rootOptions in render params
40
+ * @example
41
+ * {
42
+ * identifierPrefix: 'app-',
43
+ * onRecoverableError: (err) => console.error(err)
44
+ * }
45
+ */
46
+ defaultRootOptions?: CreateRootOptions;
37
47
  }
38
48
 
39
49
  /**
@@ -97,6 +107,15 @@ export declare interface RenderParams {
97
107
  initialState?: Record<string, unknown>;
98
108
  };
99
109
  dom: HTMLElement;
110
+ /**
111
+ * Options to pass to createRoot for React 18 and 19
112
+ * @example
113
+ * {
114
+ * identifierPrefix: 'app-',
115
+ * onRecoverableError: (err) => console.error(err)
116
+ * }
117
+ */
118
+ rootOptions?: CreateRootOptions;
100
119
  [key: string]: unknown;
101
120
  }
102
121
 
package/dist/index.es.js CHANGED
@@ -1,4 +1,4 @@
1
- var _a;
1
+ var _a, _b;
2
2
  import * as React from "react";
3
3
  import React__default, { createContext, Component, createElement, forwardRef, useRef, useState, useEffect, useContext } from "react";
4
4
  import { L as LoggerInstance, g as getRootDomDefaultClassName, p as pathJoin, R as RouterContext } from "./context-Dbqf0szX.js";
@@ -127,13 +127,13 @@ const RemoteAppWrapper = forwardRef(function(props, ref) {
127
127
  providerInfoRef.current = providerReturn;
128
128
  setInitialized(true);
129
129
  return () => {
130
- var _a2, _b, _c, _d, _e, _f, _g, _h;
130
+ var _a2, _b2, _c, _d, _e, _f, _g, _h;
131
131
  if ((_a2 = providerInfoRef.current) == null ? void 0 : _a2.destroy) {
132
132
  LoggerInstance.debug(
133
133
  `createRemoteComponent LazyComponent destroy >>>`,
134
134
  { moduleName, basename, dom: renderDom.current }
135
135
  );
136
- (_d = (_c = (_b = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _b.lifecycle) == null ? void 0 : _c.beforeBridgeDestroy) == null ? void 0 : _d.emit({
136
+ (_d = (_c = (_b2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _b2.lifecycle) == null ? void 0 : _c.beforeBridgeDestroy) == null ? void 0 : _d.emit({
137
137
  moduleName,
138
138
  dom: renderDom.current,
139
139
  basename,
@@ -157,7 +157,7 @@ const RemoteAppWrapper = forwardRef(function(props, ref) {
157
157
  };
158
158
  }, [moduleName]);
159
159
  useEffect(() => {
160
- var _a2, _b, _c, _d, _e, _f;
160
+ var _a2, _b2, _c, _d, _e, _f;
161
161
  if (!initialized || !providerInfoRef.current) return;
162
162
  let renderProps = {
163
163
  moduleName,
@@ -168,7 +168,7 @@ const RemoteAppWrapper = forwardRef(function(props, ref) {
168
168
  ...resProps
169
169
  };
170
170
  renderDom.current = rootRef.current;
171
- const beforeBridgeRenderRes = ((_c = (_b = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b.beforeBridgeRender) == null ? void 0 : _c.emit(renderProps)) || {};
171
+ const beforeBridgeRenderRes = ((_c = (_b2 = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b2.beforeBridgeRender) == null ? void 0 : _c.emit(renderProps)) || {};
172
172
  renderProps = { ...renderProps, ...beforeBridgeRenderRes.extraProps };
173
173
  providerInfoRef.current.render(renderProps);
174
174
  (_f = (_e = (_d = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _d.lifecycle) == null ? void 0 : _e.afterBridgeRender) == null ? void 0 : _f.emit(renderProps);
@@ -303,10 +303,40 @@ function createRemoteComponent(info) {
303
303
  });
304
304
  }
305
305
  const isReact18 = (_a = ReactDOM.version) == null ? void 0 : _a.startsWith("18");
306
- function createRoot(container, options) {
307
- if (isReact18) {
308
- return ReactDOM.createRoot(container, options);
306
+ const isReact19 = (_b = ReactDOM.version) == null ? void 0 : _b.startsWith("19");
307
+ let reactDOMClientPromise = null;
308
+ async function loadReactDOMClient() {
309
+ if (!isReact19) return null;
310
+ if (!reactDOMClientPromise) {
311
+ reactDOMClientPromise = import("react-dom/client");
309
312
  }
313
+ return reactDOMClientPromise;
314
+ }
315
+ function createReact19Root(container, options) {
316
+ loadReactDOMClient();
317
+ return {
318
+ render(children) {
319
+ loadReactDOMClient().then((client) => {
320
+ if (client && client.createRoot) {
321
+ const root = client.createRoot(container, options);
322
+ root.render(children);
323
+ }
324
+ });
325
+ },
326
+ unmount() {
327
+ loadReactDOMClient().then((client) => {
328
+ if (client && client.createRoot) {
329
+ const root = client.createRoot(container, options);
330
+ root.unmount();
331
+ }
332
+ });
333
+ }
334
+ };
335
+ }
336
+ function createReact18Root(container, options) {
337
+ return ReactDOM.createRoot(container, options);
338
+ }
339
+ function createReact16Or17Root(container) {
310
340
  return {
311
341
  render(children) {
312
342
  ReactDOM.render(children, container);
@@ -316,8 +346,18 @@ function createRoot(container, options) {
316
346
  }
317
347
  };
318
348
  }
349
+ function createRoot(container, options) {
350
+ if (isReact19) {
351
+ return createReact19Root(container, options);
352
+ }
353
+ if (isReact18) {
354
+ return createReact18Root(container, options);
355
+ }
356
+ return createReact16Or17Root(container);
357
+ }
319
358
  function createBridgeComponent({
320
359
  createRoot: createRoot$1 = createRoot,
360
+ defaultRootOptions,
321
361
  ...bridgeInfo
322
362
  }) {
323
363
  return () => {
@@ -341,7 +381,7 @@ function createBridgeComponent({
341
381
  };
342
382
  return {
343
383
  async render(info) {
344
- var _a2, _b, _c, _d, _e, _f;
384
+ var _a2, _b2, _c, _d, _e, _f;
345
385
  LoggerInstance.debug(`createBridgeComponent render Info`, info);
346
386
  const {
347
387
  moduleName,
@@ -349,9 +389,14 @@ function createBridgeComponent({
349
389
  basename,
350
390
  memoryRoute,
351
391
  fallback,
392
+ rootOptions,
352
393
  ...propsInfo
353
394
  } = info;
354
- const beforeBridgeRenderRes = ((_c = (_b = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b.beforeBridgeRender) == null ? void 0 : _c.emit(info)) || {};
395
+ const mergedRootOptions = {
396
+ ...defaultRootOptions,
397
+ ...rootOptions
398
+ };
399
+ const beforeBridgeRenderRes = ((_c = (_b2 = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b2.beforeBridgeRender) == null ? void 0 : _c.emit(info)) || {};
355
400
  const rootComponentWithErrorBoundary = /* @__PURE__ */ React.createElement(ErrorBoundary, { FallbackComponent: fallback }, /* @__PURE__ */ React.createElement(
356
401
  RawComponent,
357
402
  {
@@ -360,7 +405,10 @@ function createBridgeComponent({
360
405
  basename,
361
406
  memoryRoute
362
407
  },
363
- propsInfo: { ...propsInfo, ...beforeBridgeRenderRes == null ? void 0 : beforeBridgeRenderRes.extraProps }
408
+ propsInfo: {
409
+ ...propsInfo,
410
+ ...beforeBridgeRenderRes == null ? void 0 : beforeBridgeRenderRes.extraProps
411
+ }
364
412
  }
365
413
  ));
366
414
  if (bridgeInfo.render) {
@@ -370,7 +418,7 @@ function createBridgeComponent({
370
418
  } else {
371
419
  let root = rootMap.get(dom);
372
420
  if (!root) {
373
- root = createRoot$1(dom);
421
+ root = createRoot$1(dom, mergedRootOptions);
374
422
  rootMap.set(dom, root);
375
423
  }
376
424
  if ("render" in root) {
@@ -380,7 +428,7 @@ function createBridgeComponent({
380
428
  ((_f = (_e = (_d = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _d.lifecycle) == null ? void 0 : _e.afterBridgeRender) == null ? void 0 : _f.emit(info)) || {};
381
429
  },
382
430
  destroy(info) {
383
- var _a2, _b, _c;
431
+ var _a2, _b2, _c;
384
432
  const { dom } = info;
385
433
  LoggerInstance.debug(`createBridgeComponent destroy Info`, info);
386
434
  const root = rootMap.get(dom);
@@ -392,7 +440,7 @@ function createBridgeComponent({
392
440
  }
393
441
  rootMap.delete(dom);
394
442
  }
395
- (_c = (_b = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b.afterBridgeDestroy) == null ? void 0 : _c.emit(info);
443
+ (_c = (_b2 = (_a2 = instance == null ? void 0 : instance.bridgeHook) == null ? void 0 : _a2.lifecycle) == null ? void 0 : _b2.afterBridgeDestroy) == null ? void 0 : _c.emit(info);
396
444
  }
397
445
  };
398
446
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/bridge-react",
3
- "version": "0.0.0-next-20250407024707",
3
+ "version": "0.0.0-next-20250408024819",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -45,8 +45,8 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "react-error-boundary": "^4.1.2",
48
- "@module-federation/bridge-shared": "0.0.0-next-20250407024707",
49
- "@module-federation/sdk": "0.0.0-next-20250407024707"
48
+ "@module-federation/bridge-shared": "0.0.0-next-20250408024819",
49
+ "@module-federation/sdk": "0.0.0-next-20250408024819"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": ">=16.9.0",
@@ -67,7 +67,7 @@
67
67
  "typescript": "^5.2.2",
68
68
  "vite": "^5.4.12",
69
69
  "vite-plugin-dts": "^4.3.0",
70
- "@module-federation/runtime": "0.0.0-next-20250407024707"
70
+ "@module-federation/runtime": "0.0.0-next-20250408024819"
71
71
  },
72
72
  "scripts": {
73
73
  "dev": "vite",
@@ -1,25 +1,88 @@
1
1
  import ReactDOM from 'react-dom';
2
- import { CreateRootOptions, Root } from '../types';
3
2
 
4
- // ReactDOM.version is only available in React 16.13.0 and later
3
+ export interface CreateRootOptions {
4
+ identifierPrefix?: string;
5
+ onRecoverableError?: (error: unknown) => void;
6
+ transitionCallbacks?: unknown;
7
+ }
8
+
9
+ interface Root {
10
+ render(children: React.ReactNode): void;
11
+ unmount(): void;
12
+ }
13
+
14
+ // Check React version
5
15
  const isReact18 = ReactDOM.version?.startsWith('18');
16
+ const isReact19 = ReactDOM.version?.startsWith('19');
17
+
18
+ // Store the promise for async loaded ReactDOMClient
19
+ let reactDOMClientPromise: Promise<any> | null = null;
6
20
 
7
21
  /**
8
- * Creates a root for a container element compatible with both React 16 and 18
22
+ * Asynchronously load the react-dom/client module
23
+ * Only attempts to load in React 19 environment
9
24
  */
10
- export function createRoot(
25
+ async function loadReactDOMClient() {
26
+ if (!isReact19) return null;
27
+
28
+ if (!reactDOMClientPromise) {
29
+ reactDOMClientPromise = import('react-dom/client');
30
+ }
31
+
32
+ return reactDOMClientPromise;
33
+ }
34
+
35
+ /**
36
+ * Creates a root for React 19 using dynamic import of react-dom/client
37
+ */
38
+ function createReact19Root(
11
39
  container: Element | DocumentFragment,
12
40
  options?: CreateRootOptions,
13
41
  ): Root {
14
- if (isReact18) {
15
- // For React 18, use the new createRoot API
16
- // @ts-ignore - Types will be available in React 18
17
- return (ReactDOM as any).createRoot(container, options);
18
- }
42
+ // Preload react-dom/client module
43
+ loadReactDOMClient();
19
44
 
20
- // For React 16/17, simulate the new root API using render/unmountComponentAtNode
45
+ // Return a simple Root object
21
46
  return {
22
47
  render(children: React.ReactNode) {
48
+ // Try to load client again when render is called
49
+ loadReactDOMClient().then((client) => {
50
+ if (client && client.createRoot) {
51
+ const root = client.createRoot(container, options);
52
+ root.render(children);
53
+ }
54
+ });
55
+ },
56
+ unmount() {
57
+ // Try to load client again when unmount is called
58
+ loadReactDOMClient().then((client) => {
59
+ if (client && client.createRoot) {
60
+ const root = client.createRoot(container, options);
61
+ root.unmount();
62
+ }
63
+ });
64
+ },
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Creates a root for React 18 using ReactDOM.createRoot
70
+ */
71
+ function createReact18Root(
72
+ container: Element | DocumentFragment,
73
+ options?: CreateRootOptions,
74
+ ): Root {
75
+ // @ts-ignore - Types will be available in React 18
76
+ return (ReactDOM as any).createRoot(container, options);
77
+ }
78
+
79
+ /**
80
+ * Creates a root for React 16/17 using legacy APIs
81
+ */
82
+ function createReact16Or17Root(container: Element | DocumentFragment): Root {
83
+ return {
84
+ render(children: React.ReactNode) {
85
+ // @ts-ignore - React 17's render method is deprecated but still functional
23
86
  ReactDOM.render(children, container);
24
87
  },
25
88
  unmount() {
@@ -29,32 +92,101 @@ export function createRoot(
29
92
  }
30
93
 
31
94
  /**
32
- * Hydrates a container compatible with both React 16 and 18
95
+ * Creates a root for a container element compatible with React 16, 18, and 19
33
96
  */
34
- export function hydrateRoot(
97
+ export function createRoot(
35
98
  container: Element | DocumentFragment,
36
- initialChildren: React.ReactNode,
37
99
  options?: CreateRootOptions,
38
100
  ): Root {
101
+ if (isReact19) {
102
+ return createReact19Root(container, options);
103
+ }
104
+
39
105
  if (isReact18) {
40
- // For React 18, use the new hydrateRoot API
41
- // @ts-ignore - Types will be available in React 18
42
- return (ReactDOM as any).hydrateRoot(container, initialChildren, options);
106
+ return createReact18Root(container, options);
43
107
  }
44
108
 
45
- // For React 16/17, simulate the new root API using hydrate/unmountComponentAtNode
109
+ // For React 16/17
110
+ return createReact16Or17Root(container);
111
+ }
112
+
113
+ /**
114
+ * Creates a hydration root for React 19 using dynamic import of react-dom/client
115
+ */
116
+ function hydrateReact19Root(
117
+ container: Element | DocumentFragment,
118
+ initialChildren: React.ReactNode,
119
+ options?: CreateRootOptions,
120
+ ): Root {
121
+ // Preload react-dom/client module
122
+ loadReactDOMClient();
123
+
124
+ // Return a simple Root object
46
125
  return {
47
126
  render(children: React.ReactNode) {
48
- // For the initial render, use hydrate
49
- if (children === initialChildren) {
50
- ReactDOM.hydrate(children, container);
51
- } else {
52
- // For subsequent renders, use regular render
53
- ReactDOM.render(children, container);
54
- }
127
+ // Try to load client again when render is called
128
+ loadReactDOMClient().then((client) => {
129
+ if (client && client.hydrateRoot) {
130
+ const root = client.hydrateRoot(container, initialChildren, options);
131
+ root.render(children);
132
+ }
133
+ });
134
+ },
135
+ unmount() {
136
+ // Try to load client again when unmount is called
137
+ loadReactDOMClient().then((client) => {
138
+ if (client && client.hydrateRoot) {
139
+ const root = client.hydrateRoot(container, initialChildren, options);
140
+ root.unmount();
141
+ }
142
+ });
143
+ },
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Creates a hydration root for React 18 using ReactDOM.hydrateRoot
149
+ */
150
+ function hydrateReact18Root(
151
+ container: Element | DocumentFragment,
152
+ initialChildren: React.ReactNode,
153
+ options?: CreateRootOptions,
154
+ ): Root {
155
+ // @ts-ignore - Types will be available in React 18
156
+ return (ReactDOM as any).hydrateRoot(container, initialChildren, options);
157
+ }
158
+
159
+ /**
160
+ * Creates a hydration root for React 16/17 using legacy APIs
161
+ */
162
+ function hydrateReact16Or17Root(container: Element | DocumentFragment): Root {
163
+ return {
164
+ render(children: React.ReactNode) {
165
+ // @ts-ignore - React 17's hydrate method is deprecated but still functional
166
+ ReactDOM.hydrate(children, container);
55
167
  },
56
168
  unmount() {
57
169
  ReactDOM.unmountComponentAtNode(container);
58
170
  },
59
171
  };
60
172
  }
173
+
174
+ /**
175
+ * Hydrates a container compatible with React 16, 18, and 19
176
+ */
177
+ export function hydrateRoot(
178
+ container: Element | DocumentFragment,
179
+ initialChildren: React.ReactNode,
180
+ options?: CreateRootOptions,
181
+ ): Root {
182
+ if (isReact19) {
183
+ return hydrateReact19Root(container, initialChildren, options);
184
+ }
185
+
186
+ if (isReact18) {
187
+ return hydrateReact18Root(container, initialChildren, options);
188
+ }
189
+
190
+ // For React 16/17
191
+ return hydrateReact16Or17Root(container);
192
+ }
@@ -6,6 +6,7 @@ import type {
6
6
  RootType,
7
7
  DestroyParams,
8
8
  RenderParams,
9
+ CreateRootOptions,
9
10
  } from '../types';
10
11
  import { ErrorBoundary } from 'react-error-boundary';
11
12
  import { RouterContext } from './context';
@@ -15,6 +16,7 @@ import { createRoot as defaultCreateRoot } from './compat';
15
16
 
16
17
  export function createBridgeComponent<T>({
17
18
  createRoot = defaultCreateRoot,
19
+ defaultRootOptions,
18
20
  ...bridgeInfo
19
21
  }: ProviderFnParams<T>) {
20
22
  return () => {
@@ -48,9 +50,16 @@ export function createBridgeComponent<T>({
48
50
  basename,
49
51
  memoryRoute,
50
52
  fallback,
53
+ rootOptions,
51
54
  ...propsInfo
52
55
  } = info;
53
56
 
57
+ // Merge default root options with render-specific root options
58
+ const mergedRootOptions: CreateRootOptions | undefined = {
59
+ ...defaultRootOptions,
60
+ ...(rootOptions as CreateRootOptions),
61
+ };
62
+
54
63
  const beforeBridgeRenderRes =
55
64
  instance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(info) || {};
56
65
 
@@ -63,7 +72,10 @@ export function createBridgeComponent<T>({
63
72
  memoryRoute,
64
73
  }}
65
74
  propsInfo={
66
- { ...propsInfo, ...beforeBridgeRenderRes?.extraProps } as T
75
+ {
76
+ ...propsInfo,
77
+ ...(beforeBridgeRenderRes as any)?.extraProps,
78
+ } as T
67
79
  }
68
80
  />
69
81
  </ErrorBoundary>
@@ -77,7 +89,7 @@ export function createBridgeComponent<T>({
77
89
  let root = rootMap.get(dom);
78
90
  // do not call createRoot multiple times
79
91
  if (!root) {
80
- root = createRoot(dom);
92
+ root = createRoot(dom, mergedRootOptions);
81
93
  rootMap.set(dom, root);
82
94
  }
83
95
 
@@ -85,7 +97,6 @@ export function createBridgeComponent<T>({
85
97
  root.render(rootComponentWithErrorBoundary);
86
98
  }
87
99
  }
88
-
89
100
  instance?.bridgeHook?.lifecycle?.afterBridgeRender?.emit(info) || {};
90
101
  },
91
102
 
package/src/types.ts CHANGED
@@ -34,6 +34,15 @@ export interface RenderParams {
34
34
  initialState?: Record<string, unknown>;
35
35
  };
36
36
  dom: HTMLElement;
37
+ /**
38
+ * Options to pass to createRoot for React 18 and 19
39
+ * @example
40
+ * {
41
+ * identifierPrefix: 'app-',
42
+ * onRecoverableError: (err) => console.error(err)
43
+ * }
44
+ */
45
+ rootOptions?: CreateRootOptions;
37
46
  [key: string]: unknown;
38
47
  }
39
48
 
@@ -81,6 +90,16 @@ export interface ProviderFnParams<T> {
81
90
  container: Element | DocumentFragment,
82
91
  options?: CreateRootOptions,
83
92
  ) => Root;
93
+ /**
94
+ * Default options to pass to createRoot for React 18 and 19
95
+ * These options will be used when creating a root unless overridden by rootOptions in render params
96
+ * @example
97
+ * {
98
+ * identifierPrefix: 'app-',
99
+ * onRecoverableError: (err) => console.error(err)
100
+ * }
101
+ */
102
+ defaultRootOptions?: CreateRootOptions;
84
103
  }
85
104
 
86
105
  /**