---
title: "Feature Flag Routing"
description: "Route requests to different microfrontends based on feature flags for gradual rollouts and A/B testing."
canonical_url: "https://vercel.com/academy/microfrontends-on-vercel/feature-flag-routing"
md_url: "https://vercel.com/academy/microfrontends-on-vercel/feature-flag-routing.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-03-10T14:21:07.999Z"
content_type: "lesson"
course: "microfrontends-on-vercel"
course_title: "Microfrontends on Vercel"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Feature Flag Routing

# 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:

1. Test it with internal users first
2. Roll out to 10% of users
3. Gradually increase to 100%
4. 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

1. Add `flag` property to routing configuration
2. Add `runMicrofrontendsMiddleware` to default app
3. Create the new dashboard-v2 app
4. 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:

```bash
cd apps
pnpm create next-app@latest dashboard-v2 --typescript --tailwind --eslint --app --src-dir=false --import-alias="@/*"
```

Update `apps/dashboard-v2/package.json`:

```json title="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`:

```typescript title="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):

```tsx title="apps/dashboard-v2/app/app/page.tsx"
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:

```json title="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": ["/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`:

```typescript title="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:**

```bash
git add -A
git commit -m "feat: add feature flag routing for dashboard v2"
git push
```

**Test without flag (routes to original dashboard):**

1. Visit your Vercel preview URL at `/app`
2. You should see the original dashboard

**Test with flag (routes to dashboard-v2):**

1. Open DevTools console
2. Set the cookie: `document.cookie = "new-dashboard=true; path=/"`
3. Refresh the `/app` page
4. 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:

```typescript title="apps/marketing/middleware.ts"
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](https://flags-sdk.dev), share `FLAGS_SECRET` across all microfrontends in the group.

## Testing with Validation

Use the testing utility to verify flagged routing:

```typescript title="apps/marketing/__tests__/feature-flags.test.ts"
/* @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

```bash
git add -A
git commit -m "feat: add feature flag routing for dashboard v2"
```

## Done-When

- [ ] Dashboard V2 app created and configured
- [ ] `flag` property added to routing in microfrontends.json
- [ ] `runMicrofrontendsMiddleware` added 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.


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
