Quick start
This guide creates a project with one host and one remote, then renders the remote inside the host. Commands use npm; the Installation page lists the Bun and pnpm equivalents.
Requirements: Node.js 20+ and Git. See Installation.
1. Create the project
npx mfe-forge@latest init my-shop --package-manager npm
cd my-shopWhen you pass a name, init skips the interactive prompts and creates:
my-shop/
├── apps/
├── packages/
├── tools/
├── .gitignore
├── .gitattributes # added by git init step
├── README.md
├── mfeforge.config.js
├── package.json
└── tsconfig.base.jsonIt then runs git init and npm install. Add --skip-install or --skip-git to skip either step. Run init without a name to get prompts for the organization, the package manager and optional features (the GitHub Actions workflow is only written when you select it).
2. Fix the workspace glob
Apps are generated at apps/<scope>/<name>, but the root package.json lists only apps/*. Change it to:
"workspaces": ["apps/*/*", "packages/*"]Without this change npm install does not install the apps' dependencies and the dev:<scope>:<name> scripts fail. (With pnpm, create pnpm-workspace.yaml instead; see Installation.)
3. Generate a host and a remote
npx mfe generate host shop/host
npx mfe generate app shop/catalogThe output tells you which files were created. With an empty project and free ports, the host gets port 3000 and the remote 3001; check the port: lines in each vite.config.ts for the actual values.
Generating the remote after the host also updates apps/shop/host/vite.config.ts:
remotes: {
shopCatalogApp: 'http://localhost:3001/assets/remoteEntry.js',
},and writes apps/shop/host/src/remotes/declarations.d.ts with a declare module 'shopCatalogApp/App' block. Each generate also adds a root script, here dev:shop:host and dev:shop:catalog.
4. Expose the component, not the bootstrap
The app template exposes ./App as ./src/bootstrap.tsx. That file mounts the remote into #root as soon as it is imported, which would replace the host's own UI when the host imports it. Point the exposed module at the component instead, in apps/shop/catalog/vite.config.ts:
exposes: {
'./App': './src/App.tsx',
},The remote still runs standalone, because src/main.tsx imports bootstrap.tsx directly.
5. Render the remote in the host
Edit apps/shop/host/src/App.tsx. Add the import and a route:
import { LazyRemote } from '@mfe-forge/core'
const Catalog = LazyRemote(() => import('shopCatalogApp/App'), {
remoteName: 'shop/catalog',
})
// inside <Routes>, before the "*" route:
<Route path="/catalog/*" element={<Catalog />} />Optionally add { path: '/catalog', label: 'Catalog' } to navItems in src/layouts/AppLayout.tsx.
6. Install and run
npm installThe templates use @originjs/vite-plugin-federation, which only emits remoteEntry.js for a built remote. Build the remote and serve it with Vite's preview server, then start the host in dev mode:
# terminal 1: build and serve the remote on its port
npx mfe build shop/catalog
npm run preview --workspace=shop-catalog
# terminal 2: run the host
npm run dev:shop:hostOpen http://localhost:3000/catalog. The host loads http://localhost:3001/assets/remoteEntry.js and renders the catalog app. Stop terminal 1 and reload: the host shows an alert with a Try again button instead of crashing.
TIP
The package name is shop-catalog because no organization is configured. With organization: 'acme' it would be @acme/shop-catalog.
7. Check the project
npx mfe doctor
npx mfe status
npx mfe sync --checkdoctor prints one line per check and exits with code 2 if any fails. status lists every app with its port and writes mfe-forge.manifest.json. sync --check exits with 1 when a host's remotes or declarations are out of date.
Next steps
- Example project: two remotes, shared state and events, tests and a production build.
- Concepts: scopes, naming, ports and sync in detail.
- CLI reference: all commands and flags.