Skip to content

Commit f67a331

Browse files
Manually add React remote MFE to Angular host
1 parent a1e33f1 commit f67a331

10 files changed

Lines changed: 91 additions & 1 deletion

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"remote1": "http://localhost:4201/mf-manifest.json",
3-
"remote2": "http://localhost:4202/mf-manifest.json"
3+
"remote2": "http://localhost:4202/mf-manifest.json",
4+
"remote3": "http://localhost:4203/mf-manifest.json"
45
}

apps/host/src/app/app.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
<li><a routerLink="/">Home</a></li>
33
<li><a routerLink="remote1">Remote1</a></li>
44
<li><a routerLink="remote2">Remote2</a></li>
5+
<li><a routerLink="remote3">Remote3</a></li>
56
</ul>
67
<router-outlet></router-outlet>

apps/host/src/app/app.routes.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NxWelcome } from './nx-welcome';
22
import { Route } from '@angular/router';
33
import { loadRemote } from '@module-federation/enhanced/runtime';
4+
import { ReactWrapper } from './react-wrapper/react-wrapper';
45

56
export const appRoutes: Route[] = [
67
{
@@ -17,6 +18,14 @@ export const appRoutes: Route[] = [
1718
(m) => m!.remoteRoutes
1819
),
1920
},
21+
{
22+
path: 'remote3',
23+
component: ReactWrapper, // the Angular wrapper component which will load any React web component
24+
data: {
25+
elementName: 'wc-remote3', // the name of the custom element which is a React web component
26+
loadChildren: () => import('remote3/Module'), // the dynamic import of the React web component
27+
},
28+
},
2029
{
2130
path: '',
2231
component: NxWelcome,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { ReactWrapper } from './react-wrapper';
3+
4+
describe('ReactWrapper', () => {
5+
let component: ReactWrapper;
6+
let fixture: ComponentFixture<ReactWrapper>;
7+
8+
beforeEach(async () => {
9+
await TestBed.configureTestingModule({
10+
imports: [ReactWrapper],
11+
}).compileComponents();
12+
13+
fixture = TestBed.createComponent(ReactWrapper);
14+
component = fixture.componentInstance;
15+
fixture.detectChanges();
16+
});
17+
18+
it('should create', () => {
19+
expect(component).toBeTruthy();
20+
});
21+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { AfterContentInit, Component, ElementRef, inject, ViewChild } from '@angular/core';
2+
import { ActivatedRoute } from '@angular/router';
3+
4+
@Component({
5+
selector: 'app-react-wrapper',
6+
template: '<div #reactWrapper></div>',
7+
})
8+
export class ReactWrapper implements AfterContentInit {
9+
@ViewChild('reactWrapper', { read: ElementRef, static: true }) reactWrapper!: ElementRef;
10+
private route: ActivatedRoute = inject(ActivatedRoute);
11+
12+
async ngAfterContentInit(): Promise<void> {
13+
const elementName = this.route.snapshot.data['elementName'];
14+
const loader = this.route.snapshot.data['loadChildren'];
15+
await loader();
16+
const element = document.createElement(elementName);
17+
this.reactWrapper.nativeElement.appendChild(element);
18+
}
19+
}

apps/host/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"extends": "../../tsconfig.base.json",
33
"compilerOptions": {
4+
"jsx": "react-jsx",
45
"strict": true,
56
"noImplicitOverride": true,
67
"noPropertyAccessFromIndexSignature": true,

apps/remote3/src/app/app.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Uncomment this line to use CSS modules
22
// import styles from './app.module.scss';
33
import NxWelcome from './nx-welcome';
4+
import r2wc from '@r2wc/react-to-web-component';
45

56
export function App() {
67
return (
@@ -10,4 +11,15 @@ export function App() {
1011
);
1112
}
1213

14+
export function defineReactWebComponent() {
15+
// Define the new custom element with the element name for the React Web Component.
16+
if (!customElements.get('wc-remote3')) {
17+
if (!customElements.get('wc-remote3')) {
18+
// This is where we convert the React component to a Web Component
19+
customElements.define('wc-remote3', r2wc(App));
20+
}
21+
}
22+
}
23+
24+
defineReactWebComponent();
1325
export default App;

nx.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@
116116
"style": "scss",
117117
"linter": "eslint"
118118
}
119+
},
120+
"@nx/angular:component": {
121+
"style": "css"
119122
}
120123
}
121124
}

package-lock.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@nx/workspace": "21.4.1",
2929
"@playwright/test": "^1.36.0",
3030
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
31+
"@r2wc/react-to-web-component": "^2.0.4",
3132
"@schematics/angular": "~20.1.0",
3233
"@svgr/webpack": "^8.0.1",
3334
"@swc-node/register": "~1.9.1",

0 commit comments

Comments
 (0)