Configuration Basics
Add the routing configuration that tells Vercel which paths go to which apps.
Outcome
Configure microfrontends.json for routing and wrap each Next.js config with withMicrofrontends to prevent asset conflicts.
Two Configuration Files
Microfrontends require two types of configuration:
microfrontends.json- Defines routing (which paths go to which apps)next.config.tswrapper - Configures asset prefixes to prevent collisions
Fast Track
- Install
@vercel/microfrontendsin each app - Create
microfrontends.jsonin the marketing (default) app - Wrap each
next.config.tswithwithMicrofrontends
Hands-on Exercise 2.1
Configure the Acme Platform for microfrontends.
Part 1: Install the Package
Add @vercel/microfrontends to each application using pnpm's workspace filter:
pnpm add @vercel/microfrontends --filter @acme/marketing
pnpm add @vercel/microfrontends --filter @acme/docs
pnpm add @vercel/microfrontends --filter @acme/dashboardThis installs the package in all three apps.
Part 2: Create microfrontends.json
Create apps/marketing/microfrontends.json:
{
"$schema": "https://openapi.vercel.sh/microfrontends.json",
"applications": {
"@acme/marketing": {
"development": {
"fallback": "http://localhost:3000",
"local": 3000
}
},
"@acme/docs": {
"routing": [
{
"paths": ["/docs", "/docs/:path*"]
}
],
"development": {
"local": 3001
}
},
"@acme/dashboard": {
"routing": [
{
"paths": ["/app", "/app/:path*", "/settings", "/settings/:path*"]
}
],
"development": {
"local": 3002
}
}
}
}Note: application names must match the name in each app's package.json. The default app uses development.fallback instead of routing. It catches everything not claimed by other apps.
Part 3: Wrap Next.js Configs
Update each application's next.config.ts to use the wrapper.
Marketing (apps/marketing/next.config.ts):
import type { NextConfig } from "next";
import { withMicrofrontends } from "@vercel/microfrontends/next/config";
const nextConfig: NextConfig = {
// Your existing config
};
export default withMicrofrontends(nextConfig);Docs (apps/docs/next.config.ts):
import type { NextConfig } from "next";
import { withMicrofrontends } from "@vercel/microfrontends/next/config";
const nextConfig: NextConfig = {
basePath: "/docs",
};
export default withMicrofrontends(nextConfig);Dashboard (apps/dashboard/next.config.ts):
import type { NextConfig } from "next";
import { withMicrofrontends } from "@vercel/microfrontends/next/config";
const nextConfig: NextConfig = {
// No basePath - dashboard handles both /app/* and /settings/*
};
export default withMicrofrontends(nextConfig);Use basePath when all routes share a common prefix (like /docs). Dashboard handles both /app/* and /settings/*, so no single basePath works. Routes are defined directly in the file structure.
Part 4: Update App Route Structure
Child apps use basePath to set their mount point, so file paths are relative to that base. When a request hits /docs/getting-started, it's routed to the docs app (which has basePath: "/docs"), and Next.js looks for a route at /getting-started within that app.
Docs app structure (apps/docs/app/):
apps/docs/app/
├── page.tsx # handles /docs (the base path)
├── [slug]/
│ └── page.tsx # handles /docs/:slug
└── layout.tsx
Create apps/docs/app/page.tsx:
export default function DocsHomePage() {
return (
<div className="max-w-4xl mx-auto p-8">
<h1 className="text-3xl font-bold mb-2">Documentation</h1>
<p className="text-gray-600 mb-8">
Everything you need to build with Acme Platform.
</p>
<div className="grid gap-4">
<a href="/docs/getting-started" className="block p-4 border rounded-lg hover:border-blue-500">
<h2 className="font-semibold">Getting Started</h2>
<p className="text-gray-600 text-sm">Learn the basics</p>
</a>
<a href="/docs/api" className="block p-4 border rounded-lg hover:border-blue-500">
<h2 className="font-semibold">API Reference</h2>
<p className="text-gray-600 text-sm">Complete API documentation</p>
</a>
</div>
</div>
);
}Dashboard app structure (apps/dashboard/app/):
Without basePath, routes map directly to the file structure:
apps/dashboard/app/
├── app/
│ ├── page.tsx # handles /app
│ └── projects/
│ └── page.tsx # handles /app/projects
├── settings/
│ └── page.tsx # handles /settings
└── layout.tsx
Create apps/dashboard/app/app/page.tsx:
export default function DashboardPage() {
return (
<div>
<h1 className="text-2xl font-bold mb-6">Dashboard Overview</h1>
<div className="grid md:grid-cols-3 gap-4">
<div className="bg-white p-6 rounded-lg border">
<h2 className="text-gray-500 text-sm mb-1">Total Projects</h2>
<p className="text-3xl font-bold">12</p>
</div>
<div className="bg-white p-6 rounded-lg border">
<h2 className="text-gray-500 text-sm mb-1">Active Deployments</h2>
<p className="text-3xl font-bold">8</p>
</div>
</div>
</div>
);
}Create apps/dashboard/app/settings/page.tsx:
export default function SettingsPage() {
return (
<div>
<h1 className="text-2xl font-bold mb-6">Settings</h1>
<div className="bg-white rounded-lg border p-6 max-w-2xl">
<h2 className="font-semibold mb-4">Account Settings</h2>
<p className="text-gray-600">Manage your account settings here.</p>
</div>
</div>
);
}Why Asset Prefixes Matter
Without withMicrofrontends, every app serves static assets at /_next/:
marketing: /_next/static/chunks/main.js
docs: /_next/static/chunks/main.js ← CONFLICT!
dashboard: /_next/static/chunks/main.js ← CONFLICT!
The wrapper adds unique prefixes (an obfuscated hash like vc-ap-b3331f):
marketing: /_next/static/chunks/main.js
docs: /vc-ap-a1b2c3/_next/static/chunks/main.js
dashboard: /vc-ap-d4e5f6/_next/static/chunks/main.js
This prevents JavaScript and CSS from one app overwriting another's.
Try It
At this point, the apps still run independently. The configuration prepares them for the microfrontends proxy, which we'll enable in Lesson 2.3.
Run the apps:
pnpm devVisit each and verify the Header still renders correctly:
- http://localhost:3000 - Marketing
- http://localhost:3001/docs - Docs
- http://localhost:3002/app - Dashboard
Commit
git add -A
git commit -m "feat: add microfrontends configuration"Done-When
@vercel/microfrontendsinstalled in all three appsmicrofrontends.jsoncreated in marketing app with correct schema (usingpathsarray)- Application names in
microfrontends.jsonmatchpackage.jsonnames - All
next.config.tsfiles wrapped withwithMicrofrontends - Docs app has
basePath: "/docs"configured - Apps run without console errors
Troubleshooting
Asset 404s on /_next/ paths: You forgot withMicrofrontends in next.config.ts. Easy to miss.
does_not_match_schema error: You used path (string) instead of paths (array). The schema validator is picky about this.
application not_found error: The app name in microfrontends.json doesn't match package.json. Check for typos in @acme/docs vs docs.
What's Next
Configuration done. Next lesson covers path routing in detail, including the execution order that trips up most developers.
Was this helpful?