File tree 18 files changed +6417
-5073
lines changed
react-preact-runtime-typescript 18 files changed +6417
-5073
lines changed Original file line number Diff line number Diff line change
1
+ # React and Preact Integration at Runtime
2
+
3
+ This example demonstrates how to run a React-based application (shell) while consuming a remote Preact-based application (remote) dynamically at runtime.
4
+
5
+ - ` shell ` : is the host application using React and ReactDOM.
6
+ - ` remote ` : The guest application built with Preact. It provides an injector function that allows the host application (shell) to import and mount it into a specified ` <div> ` element.
7
+
8
+ # How to Run the Demo
9
+
10
+ Run ` pnpm run start ` . This will build and serve both ` shell ` and ` remote ` on ports 3001 and 3002 respectively.
11
+
12
+ - [ localhost:3001] ( http://localhost:3001/ ) (HOST)
13
+ - [ localhost:3002] ( http://localhost:3002/ ) (STANDALONE REMOTE)
Original file line number Diff line number Diff line change
1
+ {
2
+ "name" : " react-preact-runtime-typescript" ,
3
+ "description" : " This example demos host and remote applications running in isolation with React and Preact" ,
4
+ "version" : " 0.0.0" ,
5
+ "scripts" : {
6
+ "start" : " pnpm --filter react-preact-runtime-typescript-* --parallel start" ,
7
+ "build" : " pnpm --filter react-preact-runtime-typescript-* --parallel build"
8
+ }
9
+ }
Original file line number Diff line number Diff line change
1
+ {
2
+ "name" : " react-preact-runtime-typescript-remote" ,
3
+ "private" : true ,
4
+ "version" : " 1.0.0" ,
5
+ "scripts" : {
6
+ "start" : " rsbuild dev" ,
7
+ "build" : " rsbuild build" ,
8
+ "preview" : " rsbuild preview"
9
+ },
10
+ "dependencies" : {
11
+ "preact" : " ^10.25.1"
12
+ },
13
+ "devDependencies" : {
14
+ "@module-federation/rsbuild-plugin" : " ^0.8.4" ,
15
+ "@rsbuild/core" : " ^1.1.8" ,
16
+ "@rsbuild/plugin-preact" : " ^1.2.0" ,
17
+ "typescript" : " ^5.7.2"
18
+ }
19
+ }
Original file line number Diff line number Diff line change
1
+ import { pluginModuleFederation } from '@module-federation/rsbuild-plugin' ;
2
+ import { defineConfig } from '@rsbuild/core' ;
3
+ import { pluginPreact } from '@rsbuild/plugin-preact' ;
4
+
5
+ export default defineConfig ( {
6
+ plugins : [
7
+ pluginPreact ( ) ,
8
+ pluginModuleFederation ( {
9
+ name : 'remote' ,
10
+ exposes : {
11
+ './appInjector' : './src/appInjector' ,
12
+ } ,
13
+ } ) ,
14
+ ] ,
15
+ server : {
16
+ port : 3002 ,
17
+ } ,
18
+ tools : {
19
+ rspack : {
20
+ output : {
21
+ uniqueName : 'remote' ,
22
+ publicPath : 'auto' ,
23
+ } ,
24
+ } ,
25
+ } ,
26
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import LocalButton from './Button' ;
2
+
3
+ const App = ( ) => (
4
+ < div style = { { border : '1px red solid' } } >
5
+ < h1 > Remote Application - Preact</ h1 >
6
+ < LocalButton />
7
+ </ div >
8
+ ) ;
9
+
10
+ export default App ;
Original file line number Diff line number Diff line change
1
+ const Button = ( ) => < button > Remote Button</ button > ;
2
+
3
+ export default Button ;
Original file line number Diff line number Diff line change
1
+ import App from './App' ;
2
+ import { render } from 'preact' ;
3
+
4
+ let root : HTMLElement | null = null ;
5
+
6
+ export const inject = ( parentElementId : string ) => {
7
+ const parentElement = document . getElementById ( parentElementId ) ;
8
+ if ( ! parentElement ) {
9
+ console . error ( `Element with id '${ parentElementId } ' not found.` ) ;
10
+ return ;
11
+ }
12
+
13
+ root = parentElement ;
14
+ render ( < App /> , root ) ;
15
+ } ;
16
+
17
+ export const unmount = ( ) => {
18
+ if ( root ) {
19
+ render ( null , root ) ;
20
+ root = null ;
21
+ } else {
22
+ console . warn (
23
+ 'Root is not defined. Ensure inject() is called before unmount().' ,
24
+ ) ;
25
+ }
26
+ } ;
Original file line number Diff line number Diff line change
1
+ import { render } from 'preact' ;
2
+ import App from './App' ;
3
+
4
+ const root = document . getElementById ( 'root' ) ;
5
+ if ( root ) {
6
+ render ( < App /> , root ) ;
7
+ }
Original file line number Diff line number Diff line change
1
+ import ( './bootstrap' ) ;
Original file line number Diff line number Diff line change
1
+ {
2
+ "compilerOptions" : {
3
+ "lib" : [" DOM" , " ES2020" ],
4
+ "jsx" : " react-jsx" ,
5
+ "target" : " ES2020" ,
6
+ "noEmit" : true ,
7
+ "skipLibCheck" : true ,
8
+ "jsxImportSource" : " preact" ,
9
+ "useDefineForClassFields" : true ,
10
+
11
+ /* modules */
12
+ "module" : " ESNext" ,
13
+ "isolatedModules" : true ,
14
+ "resolveJsonModule" : true ,
15
+ "moduleResolution" : " Bundler" ,
16
+ "allowImportingTsExtensions" : true ,
17
+ "paths" : {
18
+ "react" : [" ./node_modules/preact/compat/" ],
19
+ "react-dom" : [" ./node_modules/preact/compat/" ]
20
+ },
21
+
22
+ /* type checking */
23
+ "strict" : true ,
24
+ "noUnusedLocals" : true ,
25
+ "noUnusedParameters" : true
26
+ },
27
+ "include" : [" src" ]
28
+ }
Original file line number Diff line number Diff line change
1
+ {
2
+ "name" : " react-preact-runtime-typescript-shell" ,
3
+ "private" : true ,
4
+ "version" : " 1.0.0" ,
5
+ "scripts" : {
6
+ "start" : " rsbuild dev" ,
7
+ "build" : " rsbuild build" ,
8
+ "preview" : " rsbuild preview"
9
+ },
10
+ "dependencies" : {
11
+ "react" : " ^19.0.0" ,
12
+ "react-dom" : " ^19.0.0"
13
+ },
14
+ "devDependencies" : {
15
+ "@module-federation/enhanced" : " ^0.8.4" ,
16
+ "@rsbuild/core" : " ^1.1.8" ,
17
+ "@rsbuild/plugin-react" : " ^1.0.7" ,
18
+ "@types/react" : " ^19.0.0" ,
19
+ "@types/react-dom" : " ^19.0.0" ,
20
+ "typescript" : " ^5.7.2"
21
+ }
22
+ }
Original file line number Diff line number Diff line change
1
+ import { defineConfig } from '@rsbuild/core' ;
2
+ import { pluginReact } from '@rsbuild/plugin-react' ;
3
+
4
+ export default defineConfig ( {
5
+ plugins : [ pluginReact ( ) ] ,
6
+ server : {
7
+ port : 3001 ,
8
+ } ,
9
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import { useEffect } from 'react' ;
2
+ import { init , loadRemote } from '@module-federation/enhanced/runtime' ;
3
+
4
+ interface RemoteModule {
5
+ inject : ( parentElementId : string ) => void ;
6
+ unmount : ( ) => void ;
7
+ }
8
+
9
+ init ( {
10
+ name : 'shell' ,
11
+ remotes : [
12
+ {
13
+ name : 'remote' ,
14
+ entry : 'http://localhost:3002/mf-manifest.json' ,
15
+ } ,
16
+ ] ,
17
+ } ) ;
18
+
19
+ const parentElementId = 'parent' ;
20
+
21
+ const App = ( ) => {
22
+ useEffect ( ( ) => {
23
+ let unmountRemote : ( ) => void ;
24
+
25
+ const loadRemoteApp = async ( ) => {
26
+ try {
27
+ const module = await loadRemote < RemoteModule > ( 'remote/appInjector' ) ;
28
+ if ( ! module ) return ;
29
+ const { inject, unmount } = module ;
30
+ unmountRemote = unmount ;
31
+
32
+ inject ( parentElementId ) ;
33
+ } catch ( error ) {
34
+ console . error ( 'Error loading the Remote:' , error ) ;
35
+ }
36
+ } ;
37
+
38
+ loadRemoteApp ( ) ;
39
+
40
+ return ( ) => {
41
+ if ( unmountRemote ) unmountRemote ( ) ;
42
+ } ;
43
+ } , [ ] ) ;
44
+
45
+ // Remote will be injected in the div with parentElementId
46
+ return (
47
+ < div >
48
+ < h1 > Host Application - React</ h1 >
49
+ < h2 > Remote</ h2 >
50
+ < div id = { parentElementId } />
51
+ </ div >
52
+ ) ;
53
+ } ;
54
+
55
+ export default App ;
Original file line number Diff line number Diff line change
1
+ import React from 'react' ;
2
+ import ReactDOM from 'react-dom/client' ;
3
+ import App from './App' ;
4
+
5
+ const rootEl = document . getElementById ( 'root' ) ;
6
+ if ( rootEl ) {
7
+ const root = ReactDOM . createRoot ( rootEl ) ;
8
+ root . render (
9
+ < React . StrictMode >
10
+ < App />
11
+ </ React . StrictMode > ,
12
+ ) ;
13
+ }
Original file line number Diff line number Diff line change
1
+ /// <reference types="@rsbuild/core/types" />
Original file line number Diff line number Diff line change
1
+ import ( './bootstrap' ) ;
Original file line number Diff line number Diff line change
1
+ {
2
+ "compilerOptions" : {
3
+ "lib" : [" DOM" , " ES2020" ],
4
+ "jsx" : " react-jsx" ,
5
+ "target" : " ES2020" ,
6
+ "noEmit" : true ,
7
+ "skipLibCheck" : true ,
8
+ "useDefineForClassFields" : true ,
9
+
10
+ /* modules */
11
+ "module" : " ESNext" ,
12
+ "isolatedModules" : true ,
13
+ "resolveJsonModule" : true ,
14
+ "moduleResolution" : " Bundler" ,
15
+ "allowImportingTsExtensions" : true ,
16
+
17
+ /* type checking */
18
+ "strict" : true ,
19
+ "noUnusedLocals" : true ,
20
+ "noUnusedParameters" : true
21
+ },
22
+ "include" : [" src" ]
23
+ }
You can’t perform that action at this time.
0 commit comments