Feature Flag Routing
Route users to different applications based on a flag. Roll out a new dashboard to 10% of users, or A/B test a redesign.
Outcome
Configure the microfrontends routing to use feature flags for dynamic routing decisions.
The Use Case
You've built a new dashboard in a separate microfrontend. You want to:
- Test it with internal users first
- Roll out to 10% of users
- Gradually increase to 100%
- Remove the old dashboard
Without feature flags, you'd have to do a big-bang migration. With flags, you control the rollout.
How It Works
With feature flag routing, flagged paths go to the default application first, which decides where to route:
Request: /app/dashboard
1. Microfrontends sees /app is flagged
2. Routes to default app (marketing) instead of dashboard
3. Default app middleware checks flag
4. If flag enabled: routes to dashboard-v2
5. If flag disabled: routes to dashboard (original)
This is different from regular routing where paths go directly to the matching app.
Fast Track
- Add
flagproperty to routing configuration - Add
runMicrofrontendsMiddlewareto default app - Create the new dashboard-v2 app
- Test both variants
Hands-on Exercise 4.1
Add feature flag routing for a new dashboard version.
Part 1: Create Dashboard V2 App
Create a simplified new dashboard:
cd apps
pnpm create next-app@latest dashboard-v2 --typescript --tailwind --eslint --app --src-dir=false --import-alias="@/*"Update apps/dashboard-v2/package.json:
{
"name": "@acme/dashboard-v2",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "next dev --port $(microfrontends port)",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "16.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@acme/ui": "workspace:*",
"@vercel/microfrontends": "latest"
}
}Create apps/dashboard-v2/next.config.ts:
import type { NextConfig } from "next";
import { withMicrofrontends } from "@vercel/microfrontends/next/config";
const nextConfig: NextConfig = {};
export default withMicrofrontends(nextConfig);Create apps/dashboard-v2/app/app/page.tsx (note the nested structure):
import { Header } from "@acme/ui";
export default function NewDashboardPage() {
return (
<div>
<Header />
<main className="p-8">
<div className="rounded-lg bg-gradient-to-r from-blue-500 to-purple-600 p-8 text-white">
<h1 className="text-4xl font-bold">Dashboard V2</h1>
<p className="mt-4 text-lg">
Welcome to the redesigned dashboard experience!
</p>
</div>
</main>
</div>
);
}Part 2: Update Microfrontends Configuration
Update apps/marketing/microfrontends.json with the feature flag:
{
"$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": ["/settings", "/settings/:path*"] },
{
"paths": ["/app", "/app/:path*"],
"flag": "new-dashboard"
}
],
"development": {
"local": 3002
}
},
"@acme/dashboard-v2": {
"routing": [
{
"paths": ["/app", "/app/:path*"],
"flag": "new-dashboard"
}
],
"development": {
"local": 3003
}
}
},
"options": {
"localProxyPort": 3024
}
}Both dashboard apps claim /app/*, but the flag property determines which one gets the request based on the flag evaluation.
Part 3: Add Microfrontends Middleware
The default app (marketing) needs middleware to handle flagged routing decisions.
Create apps/marketing/middleware.ts:
import type { NextRequest } from "next/server";
import { runMicrofrontendsMiddleware } from "@vercel/microfrontends/next/middleware";
export async function middleware(request: NextRequest) {
const response = await runMicrofrontendsMiddleware({
request,
flagValues: {
"new-dashboard": async () => {
// Simple cookie-based flag for demo
const cookie = request.cookies.get("new-dashboard");
return cookie?.value === "true";
},
},
});
if (response) {
return response;
}
}
export const config = {
matcher: [
// Required for prefetch optimizations
"/.well-known/vercel/microfrontends/client-config",
// Flagged paths
"/app",
"/app/:path*",
],
};Important: The middleware matcher must include:
/.well-known/vercel/microfrontends/client-config- For prefetch optimizations- All flagged paths - So middleware runs for these requests
Part 4: Deploy and Test
Feature flag routing doesn't work with the local proxy. You'll get "Duplicate path" errors. Deploy to Vercel to test it.
Deploy to Vercel:
git add -A
git commit -m "feat: add feature flag routing for dashboard v2"
git pushTest without flag (routes to original dashboard):
- Visit your Vercel preview URL at
/app - You should see the original dashboard
Test with flag (routes to dashboard-v2):
- Open DevTools console
- Set the cookie:
document.cookie = "new-dashboard=true; path=/" - Refresh the
/apppage - You should see Dashboard V2 with the gradient hero
Local development workaround:
For local development, you can only run one version at a time. Comment out either @acme/dashboard or @acme/dashboard-v2 in microfrontends.json to test each app individually
Production Feature Flags
In production, integrate with a feature flag service:
import type { NextRequest } from "next/server";
import { runMicrofrontendsMiddleware } from "@vercel/microfrontends/next/middleware";
import { getFlag } from "@vercel/flags/next";
export async function middleware(request: NextRequest) {
const response = await runMicrofrontendsMiddleware({
request,
flagValues: {
"new-dashboard": async () => {
// Use Vercel Flags or any flag service
return await getFlag("new-dashboard", request);
},
},
});
if (response) {
return response;
}
}If using the Flags SDK, share FLAGS_SECRET across all microfrontends in the group.
Testing with Validation
Use the testing utility to verify flagged routing:
/* @jest-environment node */
import { validateMiddlewareOnFlaggedPaths } from "@vercel/microfrontends/next/testing";
import { middleware } from "../middleware";
describe("feature flag routing", () => {
test("routes correctly for flagged paths", async () => {
await expect(
validateMiddlewareOnFlaggedPaths("./microfrontends.json", middleware)
).resolves.not.toThrow();
});
});Commit
git add -A
git commit -m "feat: add feature flag routing for dashboard v2"Done-When
- Dashboard V2 app created and configured
flagproperty added to routing in microfrontends.jsonrunMicrofrontendsMiddlewareadded to default app- Middleware matcher includes flagged paths
- Understand that feature flag routing requires Vercel deployment
- (After deploy) Can toggle between dashboards via cookie
Rollout Strategy
| Phase | Flag Value | Traffic |
|---|---|---|
| Internal testing | Specific users | ~1% |
| Beta | 10% rollout | 10% |
| Gradual | 50% rollout | 50% |
| Full | 100% rollout | 100% |
| Cleanup | Remove flag, update routing | 100% to v2 |
What's Next
What if you're migrating from a legacy system that isn't on Vercel? Next: the strangler fig pattern for incremental migration.
Was this helpful?