Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions multiple-share-scope/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Module Federation - Multiple Share Scope Example

This example demonstrates how to use **multiple share scopes** in Module Federation to run **React 17** and **React 18** simultaneously in the same host application.

## Architecture

| Application | Port | Description |
| ----------------- | ---- | ------------------------------------------------ |
| `host` | 8080 | Host app consuming both React 17 and React 18 remotes |
| `provider-react-17` | 8081 | Remote exposing components built with React 17 |
| `provider-react-18` | 8082 | Remote exposing components built with React 18 |

## How It Works

The host defines two share scopes:

- **`default`** scope — shares React 17 (`react17` / `react-dom17` aliases)
- **`react18`** scope — shares React 18 (`react` / `react-dom`)

Each remote is associated with the appropriate scope:

- `provider17` uses the `default` scope (React 17)
- `provider18` uses both `['react18', 'default']` scopes (React 18)

This allows isolated React instances per version, preventing version conflicts.

## Running the Demo

Install dependencies:

```bash
pnpm install
```

Start all applications in development mode:

```bash
pnpm start
```

- [localhost:8080](http://localhost:8080/) — Host
- [localhost:8081](http://localhost:8081/) — Provider (React 17)
- [localhost:8082](http://localhost:8082/) — Provider (React 18)

## Build & Preview

```bash
pnpm build
pnpm serve
```
35 changes: 35 additions & 0 deletions multiple-share-scope/host/@mf-types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { PackageType as PackageType_0,RemoteKeys as RemoteKeys_0 } from './provider17/apis.d.ts';
import type { PackageType as PackageType_1,RemoteKeys as RemoteKeys_1 } from './provider18/apis.d.ts';
declare module "@module-federation/runtime" {
type RemoteKeys = RemoteKeys_0 | RemoteKeys_1;
type PackageType<T, Y=any> = T extends RemoteKeys_0 ? PackageType_0<T> :
T extends RemoteKeys_1 ? PackageType_1<T> :
Y ;
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}
declare module "@module-federation/enhanced/runtime" {
type RemoteKeys = RemoteKeys_0 | RemoteKeys_1;
type PackageType<T, Y=any> = T extends RemoteKeys_0 ? PackageType_0<T> :
T extends RemoteKeys_1 ? PackageType_1<T> :
Y ;
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}
declare module "@module-federation/runtime-tools" {
type RemoteKeys = RemoteKeys_0 | RemoteKeys_1;
type PackageType<T, Y=any> = T extends RemoteKeys_0 ? PackageType_0<T> :
T extends RemoteKeys_1 ? PackageType_1<T> :
Y ;
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}
declare module "@module-federation/modern-js/runtime" {
type RemoteKeys = RemoteKeys_0 | RemoteKeys_1;
type PackageType<T, Y=any> = T extends RemoteKeys_0 ? PackageType_0<T> :
T extends RemoteKeys_1 ? PackageType_1<T> :
Y ;
export function loadRemote<T extends RemoteKeys,Y>(packageName: T): Promise<PackageType<T, Y>>;
export function loadRemote<T extends string,Y>(packageName: T): Promise<PackageType<T, Y>>;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './compiled-types/src/components/LandingPage';
export { default } from './compiled-types/src/components/LandingPage';
3 changes: 3 additions & 0 deletions multiple-share-scope/host/@mf-types/provider17/apis.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

export type RemoteKeys = 'provider17/LandingPage';
type PackageType<T> = T extends 'provider17/LandingPage' ? typeof import('provider17/LandingPage') :any;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// <reference types="react" />
declare const LandingPage: (props: any) => JSX.Element;
export default LandingPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './compiled-types/src/components/LandingPage';
export { default } from './compiled-types/src/components/LandingPage';
3 changes: 3 additions & 0 deletions multiple-share-scope/host/@mf-types/provider18/apis.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

export type RemoteKeys = 'provider18/LandingPage';
type PackageType<T> = T extends 'provider18/LandingPage' ? typeof import('provider18/LandingPage') :any;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// <reference types="react" />
declare const LandingPage: (props: any) => JSX.Element;
export default LandingPage;
14 changes: 14 additions & 0 deletions multiple-share-scope/host/modern.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { appTools, defineConfig } from '@modern-js/app-tools';
import pluginMF from '@module-federation/modern-js';

export default defineConfig({
runtime: {
router: true,
},
plugins: [
appTools({
bundler: 'webpack', // Set to 'webpack' to enable webpack
}),
pluginMF(),
],
});
36 changes: 36 additions & 0 deletions multiple-share-scope/host/module-federation.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createModuleFederationConfig } from '@module-federation/modern-js';

export default createModuleFederationConfig({
name: 'host',
remotes: {
provider17: {
external: 'provider17@http://localhost:8081/mf-manifest.json',
},
provider18: {
external: 'provider18@http://localhost:8082/mf-manifest.json',
shareScope: ['react18', 'default'],
},
},
shared: {
react: {
singleton: true,
shareScope: 'react18',
},
'react-dom': {
singleton: true,
shareScope: 'react18',
},
react17: {
shareKey: 'react',
request: 'react17',
shareScope: 'default',
singleton: true,
},
'react-dom17': {
shareKey: 'react-dom',
request: 'react-dom17',
shareScope: 'default',
singleton: true,
},
},
});
27 changes: 27 additions & 0 deletions multiple-share-scope/host/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@multiple-share-scope/host",
"version": "1.0.0",
"scripts": {
"dev": "modern dev",
"build": "modern build",
"start": "modern start",
"serve": "modern serve"
},
"dependencies": {
"@modern-js/runtime": "2.66.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-dom17": "npm:react-dom@^17",
"react17": "npm:react@^17"
},
"devDependencies": {
"@modern-js/app-tools": "2.66.0",
"@module-federation/modern-js": "2.1.0",
"@modern-js/tsconfig": "2.66.0",
"@types/node": "~16.11.7",
"@types/react": "~18.2.22",
"@types/react-dom": "~18.2.7",
"rimraf": "~3.0.2",
"typescript": "~5.0.4"
}
}
2 changes: 2 additions & 0 deletions multiple-share-scope/host/src/modern-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types='@modern-js/app-tools/types' />
/// <reference types='@modern-js/runtime/types' />
134 changes: 134 additions & 0 deletions multiple-share-scope/host/src/routes/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
html,
body {
padding: 0;
margin: 0;
font-family: PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}

p {
margin: 0;
}

* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
}

.container-box {
min-height: 100vh;
max-width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 10px;
}

main {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

.title {
display: flex;
margin: 4rem 0 4rem;
align-items: center;
font-size: 4rem;
font-weight: 600;
}

.logo {
width: 3.6rem;
margin: 0 1.6rem;
}

.name {
background: -webkit-linear-gradient(315deg, #788ec9 25%, #5978c0);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

.description {
text-align: center;
line-height: 1.5;
font-size: 1.3rem;
color: #1b3a42;
margin-bottom: 5rem;
}

.code {
background: #fafafa;
border-radius: 12px;
padding: 0.6rem 0.9rem;
font-size: 1.05rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}

.container-box .grid {
display: grid;
grid-template-columns: repeat(3, 33.33%);
grid-row-gap: 20px;
grid-column-gap: 20px;
width: 1100px;
margin-top: 3rem;
align-items: center;
justify-content: center;
}

.card {
background: #f9f9f9;
border: 1px solid transparent;
transition: all.3s;
padding: 2rem;
border-radius: 2rem;
display: flex;
flex-direction: column;
justify-content: center;
color: inherit;
text-decoration: none;
transition: 0.15s ease;
}

.card-icon {
display: flex;
align-items: center;
justify-content: center;
}

.card-icon-text {
text-align: center;
height: 3rem;
font-size: 1.875rem;
line-height: 2.25rem;
}

.card h2 {
align-items: center;
font-size: inherit;
font-weight: 700;
margin: 0;
padding: 0;
text-align: center;
}

.card p {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
padding-top: 0.5rem;
color: rgba(60, 60, 60, 0.66);
}

.landing-page {
padding: 20px;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
12 changes: 12 additions & 0 deletions multiple-share-scope/host/src/routes/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React17 from 'react17';
import React from 'react';
console.log(React17, React);
import { Outlet } from '@modern-js/runtime/router';

const Layout = (): JSX.Element => (
<div>
<Outlet />
</div>
);

export default Layout;
13 changes: 13 additions & 0 deletions multiple-share-scope/host/src/routes/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import './index.css';
import Provider17 from 'provider17/LandingPage';
import Provider18 from 'provider18/LandingPage';

const Index = (): JSX.Element => (
<div className="container-box">
<Provider17 />
<Provider18 />
</div>
);

export default Index;
16 changes: 16 additions & 0 deletions multiple-share-scope/host/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "@modern-js/tsconfig/base",
"compilerOptions": {
"declaration": false,
"jsx": "preserve",
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"],
"@shared/*": ["./shared/*"],
"*": ["./@mf-types/*"]
},
"types": ["react", "react-dom"]
},
"include": ["src", "shared", "config", "modern.config.ts", "module-federation.config.ts"],
"exclude": ["**/node_modules"]
}
16 changes: 16 additions & 0 deletions multiple-share-scope/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "multiple-share-scope",
"description": "Module Federation example demonstrating multiple share scopes to run React 17 and React 18 simultaneously.",
"private": true,
"version": "1.0.0",
"scripts": {
"start": "concurrently \"cd ./host && pnpm dev\" \"cd ./provider-react-17 && pnpm dev\" \"cd ./provider-react-18 && pnpm dev\"",
"build": "concurrently \"cd ./host && pnpm build\" \"cd ./provider-react-17 && pnpm build\" \"cd ./provider-react-18 && pnpm build\"",
"serve": "concurrently \"cd ./host && pnpm serve\" \"cd ./provider-react-17 && pnpm serve\" \"cd ./provider-react-18 && pnpm serve\"",
"clean": "concurrently \"cd ./host && pnpm clean\" \"cd ./provider-react-17 && pnpm clean\" \"cd ./provider-react-18 && pnpm clean\"",
"e2e:ci": "pnpm run build && echo \"No e2e tests yet\""
},
"devDependencies": {
"concurrently": "8.2.2"
}
}
Loading
Loading