Overview
Module Federation enables multiple independently deployed frontends to share code and dependencies. Build scalable micro frontend architectures.
Benefits
- Independent Deployment: Deploy apps without coordinating
- Shared Dependencies: Reduce bundle sizes by sharing packages
- Dynamic Loading: Load applications at runtime
- Team Autonomy: Different teams own different apps
- Shared Component Libraries: Use common components
Setup
npm install -D @originjs/vite-plugin-federation
Host Application
// vite.config.ts - host app
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@originjs/vite-plugin-federation'
export default defineConfig({
plugins: [
react(),
federation({
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button.tsx',
'./useStore': './src/hooks/useStore.ts'
},
shared: ['react', 'react-dom']
})
],
build: {
target: 'esnext',
minify: false,
cssCodeSplit: false
}
})
Remote Application
// vite.config.ts - remote app
federation({
name: 'products',
filename: 'remoteEntry.js',
exposes: {
'./ProductList': './src/pages/ProductList.tsx',
'./useProducts': './src/hooks/useProducts.ts'
},
remotes: {
hostApp: 'http://localhost:3000/assets/remoteEntry.js'
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
})
Use Remote Components
import React, { Suspense } from 'react'
const RemoteProductList = React.lazy(() =>
import('products/ProductList')
)
export function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<RemoteProductList />
</Suspense>
)
}
Dynamic Loading
// Load remote app at runtime
async function loadRemoteApp(url: string) {
const container = document.createElement('div')
document.body.appendChild(container)
const { mount } = await import(`${url}/remoteEntry.js`)
mount(container)
}
await loadRemoteApp('http://localhost:3001')
Module Federation reduces frontend bundle sizes by 60-70% through dependency sharing.