Micro-frontends skill for Codex
Split the frontend only when teams truly need it. Codex starts by questioning whether independent deploys are actually needed, and recommends a modular monorepo when they are not. When they are, it compares federation, import maps, web components, route splits and iframes, then implements the choice with singleton React and design-system sharing, typed exposed modules, namespaced events, CSS isolation, pinned remote versions and fallbacks so one failing remote never blanks the shell.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- micro-frontends Installs to ~/.agents/skills/micro-frontends/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install micro-frontends The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project micro-frontends Installs to .agents/skills/micro-frontends/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/micro-frontends.zipunzip -o micro-frontends.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) micro-frontends PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $micro-frontends, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Decides whether and how to split a frontend across teams - build-time packages, Module Federation 2 (webpack, Rspack, Vite), import maps and Native Federation, web components, iframes or server-side composition - then implements the chosen integration with shared singletons, versioned contracts, routing ownership, CSS isolation and failure fallbacks. Use when several teams ship one web app, when evaluating Module Federation, when a remote breaks the host, or when duplicate React copies and style leaks appear.
Use it when
- Several teams ship parts of one web app and block each other's releases
- Evaluating or implementing Module Federation, import maps or web component composition
- A remote breaks the host, loads duplicate dependencies or leaks styles
Use something else when
- One team owns the app and wants better structure: use monorepo-tooling
- You are deciding backend service boundaries rather than frontend: use microservice-boundaries
How to check the result
Each remote runs standalone and inside the shell, the network tab shows a single react-dom chunk, and blocking a remote's URL renders its fallback while the rest of the app keeps working.
The mistake it prevents
Declaring React as a singleton without requiredVersion, which hides version skew until hooks fail in production.
Micro-frontends are an organisational tool, not a performance one. A good outcome lets teams deploy independently without coordinating releases, while users see one fast, consistent app. If teams do not need independent deploys, a well-structured monorepo is cheaper and should be the recommendation.
Step 1: challenge the premise
Ask (or find in docs and AGENTS.md) before designing anything:
- How many teams own UI, and how often do they block each other’s releases today?
- Do they share a framework and design system, or genuinely differ?
- Is the pain build time, deploy coupling, or code ownership?
| Answer | Recommendation |
|---|---|
| One or two teams, same release train | Modular monorepo (pnpm workspaces + Nx/Turborepo), no runtime composition |
| Several teams, same framework, blocked on releases | Module Federation with a shared shell |
| Several teams, different frameworks or long-lived legacy | Route-level composition: separate apps per path behind a reverse proxy, or web components |
| Embedding a third-party or untrusted UI | iframe with postMessage contract |
| Content sites, SEO critical, mostly static | Server-side or edge composition (fragments assembled per request) |
Write the decision down as an ADR with the rejected options; the trade-offs are what future teams need.
Integration options compared
| Approach | Independent deploy | Shared deps | Isolation | Main cost |
|---|---|---|---|---|
| Build-time npm packages | No (host rebuild) | Yes, deduped | Low | Release coordination |
| Module Federation 2 | Yes | Runtime negotiated singletons | Low (same JS realm) | Version skew, build tooling coupling |
| Import maps / Native Federation | Yes | Via import map | Low | Browser ESM only, smaller ecosystem |
| Web components | Yes | Usually duplicated per app | Shadow DOM for styles | Bundle duplication, framework interop |
| Route-level split (reverse proxy) | Yes | None | High | Full page loads between areas |
| iframes | Yes | None | Very high | Layout, focus, a11y, performance |
Module Federation 2 setup
Use @module-federation/enhanced (webpack/Rspack) or @module-federation/vite. Install
needs network. Remote:
// checkout/rspack.config.js (webpack is the same shape)
const { ModuleFederationPlugin } = require('@module-federation/enhanced/rspack');
const deps = require('./package.json').dependencies;
module.exports = {
output: { uniqueName: 'checkout', publicPath: 'auto' },
plugins: [
new ModuleFederationPlugin({
name: 'checkout',
filename: 'remoteEntry.js',
exposes: { './CheckoutPage': './src/CheckoutPage.tsx' },
shared: {
react: { singleton: true, requiredVersion: deps.react },
'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
'@acme/design-system': { singleton: true, requiredVersion: deps['@acme/design-system'] },
},
dts: true, // generate and consume federated types
}),
],
};
Host:
new ModuleFederationPlugin({
name: 'shell',
remotes: {
// manifest URL enables runtime discovery, preloading and type hints
checkout: 'checkout@https://cdn.example.com/checkout/mf-manifest.json',
},
shared: { react: { singleton: true, requiredVersion: deps.react }, 'react-dom': { singleton: true, requiredVersion: deps['react-dom'] } },
});
Consume with lazy loading and a boundary:
const CheckoutPage = lazy(() => import('checkout/CheckoutPage'));
<ErrorBoundary fallback={<CheckoutUnavailable />}>
<Suspense fallback={<PageSkeleton />}>
<CheckoutPage cartId={cartId} onComplete={goToConfirmation} />
</Suspense>
</ErrorBoundary>
For per-environment remote URLs, register remotes at runtime from a config the shell fetches (the Module Federation runtime API), instead of rebuilding the host per environment.
Contracts between teams
- Props or mount API: an exposed module exports a component or a
mount(el, props) => unmountfunction. That signature is a public API: version it, type it (dts), and never break it without a deprecation window. - Events: cross-app communication through
CustomEventonwindowwith namespaced names (checkout:completed) and a typed payload, or the URL. Never a shared mutable global store between independently deployed apps. - Routing: the shell owns top-level routes; each micro-frontend owns everything
under its prefix (
/checkout/*) and uses the shell’s history instance or basename. - Auth: the shell obtains the session; remotes receive a token getter, never read cookies or storage keys owned by another team.
- Design system: one shared package as a singleton, versioned with semver; visual drift is the most visible micro-frontend failure.
CSS isolation
| Technique | Use when |
|---|---|
| CSS Modules or CSS-in-JS with unique prefixes | Default for federated React apps |
Cascade layers (@layer shell, remote) | Shell styles must win predictably |
Prefixed utility classes (Tailwind prefix) | Several apps use Tailwind with different configs |
| Shadow DOM (web components) | Hard isolation is required |
Global resets and body styles belong only to the shell. Grep remotes for :root,
html, body and * selectors.
Failure handling
- Every remote load has a timeout and a fallback UI; a down remote must not blank the shell.
- Log remote load failures with the remote name and version to monitoring.
- Pin remotes by version in production (versioned manifest URL or a deployment registry); “latest” remotes mean the host can break without any deploy of its own.
- Keep a rollback path per remote: redeploying the previous remote version must be one step.
Performance
- Shared singletons with incompatible
requiredVersionload a second copy (or warn and break hooks). Check the network tab for duplicatereact-domchunks, and inspect the MF2 runtime’s__FEDERATION__global (or the Module Federation Chrome devtools extension) to see which version of each shared package won. - Preload the remote entry for the likely next route; do not eagerly load every remote.
- Budget each remote’s initial JS; one team’s heavy dependency taxes every page it appears on.
Testing
- Each remote runs standalone with a dev shell for local work and its own tests.
- Contract tests on the exposed API (types plus a render test with the documented props).
- An end-to-end smoke suite in the integrated environment that loads every remote, run on every remote deploy, with the ability to block the deploy.
Gotchas
publicPathwrong on the remote: the entry loads but chunks 404 from the host’s origin.singleton: truewithoutrequiredVersionhides version skew until hooks fail at runtime.- React context does not cross separately bundled React copies; a duplicated React means “Invalid hook call” or missing providers.
- CORS and CSP must allow the remote origins for scripts and chunk loading.
- SSR with federation multiplies complexity; validate the framework’s support (Next.js support is limited) before promising it.
- Deploying the host and remotes in one pipeline step defeats the purpose; it is a distributed monolith.
Deliverable
## Micro-frontend decision
- Context: teams, release pain, frameworks
- Decision: <approach>, rejected: <options + reason>
- Contracts: exposed modules and signatures, events, route ownership
- Shared singletons and version policy
- Failure handling: timeouts, fallbacks, version pinning, rollback
- Rollout: first remote to extract, success metrics (deploy frequency, lead time)
Production rollouts of new remotes or changes to the shell’s remote registry affect all users: confirm with the user before deploying them.
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Micro-frontends"
short_description: "Split the frontend only when teams truly need it"
default_prompt: "$micro-frontends Should we use Module Federation for our three frontend teams? Give me a recommendation" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.