Module federation with vite

From Logic Wiki
Jump to: navigation, search


Create the apps

bun create vite host --template react-ts
bun create vite client-a --template react-ts
bun create vite client-b --template react-ts

Add vite-plugin-federations

cd host
bun add -d @originjs/vite-plugin-federation

cd ../client-a
bun add -d @originjs/vite-plugin-federation

cd ../client-b
bun add -d @originjs/vite-plugin-federation

Remote App (client-a)

client-a/vite.config.ts

import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import federation from "@originjs/vite-plugin-federation"

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: "client_a",
      filename: "remoteEntry.js",
      exposes: {
        "./Button": "./src/Button.tsx",
      },
      shared: ["react", "react-dom"],
    }),
  ],
  build: {
    target: "esnext",
    minify: false,
  },
  server: {
    port: 5001,
  },
})

client-a/src/Button.tsx

export default function Button() {
  return (
    <button style={{ padding: 10 }}>
      Client A Button
    </button>
  )
}

client-a/src/main.tsx

import React from "react"
import ReactDOM from "react-dom/client"

function App() {
  return <h2>Client A running</h2>
}

ReactDOM.createRoot(document.getElementById("root")!).render(<App />)

Remote App (client-b)

client-b/vite.config.ts

import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import federation from "@originjs/vite-plugin-federation"

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: "client_b",
      filename: "remoteEntry.js",
      exposes: {
        "./Card": "./src/Card.tsx",
      },
      shared: ["react", "react-dom"],
    }),
  ],
  build: {
    target: "esnext",
    minify: false,
  },
  server: {
    port: 5002,
  },
})

client-b/src/Card.tsx

export default function Card() {
  return (
    <div style={{ border: "1px solid black", padding: 10 }}>
      Client B Card
    </div>
  )
}

Host App

host/vite.config.ts

import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import federation from "@originjs/vite-plugin-federation"

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: "host",
      remotes: {
        client_a: "http://localhost:5001/assets/remoteEntry.js",
        client_b: "http://localhost:5002/assets/remoteEntry.js",
      },
      shared: ["react", "react-dom"],
    }),
  ],
  server: {
    port: 5000,
  },
})

Host React App

host/src/App.tsx

import React, { Suspense } from "react"

const RemoteButton = React.lazy(() => import("client_a/Button"))
const RemoteCard = React.lazy(() => import("client_b/Card"))

export default function App() {
  return (
    <div>
      <h1>Host App</h1>

      <Suspense fallback="Loading Button...">
        <RemoteButton />
      </Suspense>

      <Suspense fallback="Loading Card...">
        <RemoteCard />
      </Suspense>
    </div>
  )
}

TypeScript Declarations (Important)

host/src/remote.d.ts

declare module "client_a/Button"
declare module "client_b/Card"

Without this, TypeScript complains.

Run Everything with Bun

terminal 1

cd client-a
bun run dev

terminal 2

cd client-b
bun run dev

terminal 3

cd host
bun run dev