---
title: "Project Structure"
description: "Create the Next.js applications and shared packages that will become your microfrontends."
canonical_url: "https://vercel.com/academy/microfrontends-on-vercel/project-structure"
md_url: "https://vercel.com/academy/microfrontends-on-vercel/project-structure.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-09T07:50:11.500Z"
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>

# Project Structure

# Project Structure for Microfrontends

Create three Next.js apps and wire them to a shared UI package.

## Outcome

Create three Next.js applications (marketing, docs, dashboard) and a shared UI package with a Header component.

## The Target Structure

```
acme-platform/
├── apps/
│   ├── marketing/          # Default app (/, /pricing, /about)
│   │   ├── app/
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx
│   │   │   ├── pricing/page.tsx
│   │   │   └── about/page.tsx
│   │   ├── next.config.ts
│   │   └── package.json
│   ├── docs/               # Child app (/docs/*)
│   │   ├── app/
│   │   │   ├── layout.tsx
│   │   │   └── docs/           # Routes match /docs paths
│   │   │       ├── page.tsx    # /docs
│   │   │       └── [slug]/page.tsx  # /docs/:slug
│   │   ├── next.config.ts
│   │   └── package.json
│   └── dashboard/          # Child app (/app/*, /settings/*)
│       ├── app/
│       │   ├── layout.tsx
│       │   ├── app/            # Routes match /app paths
│       │   │   └── page.tsx    # /app
│       │   └── settings/       # Routes match /settings paths
│       │       └── page.tsx    # /settings
│       ├── next.config.ts
│       └── package.json
├── packages/
│   └── ui/                 # Shared components
│       ├── src/
│       │   ├── header.tsx
│       │   └── index.ts
│       ├── package.json
│       └── tsconfig.json
├── turbo.json
└── package.json
```

Child apps must have their file structure match the routed paths. The docs app receives `/docs/*` requests, so it needs routes at `app/docs/*`. The dashboard receives `/app/*` and `/settings/*`, so it needs routes at `app/app/*` and `app/settings/*`.

## Fast Track

1. Create three Next.js apps in `apps/`
2. Create the shared UI package in `packages/`
3. Wire up dependencies and verify the Header renders in all apps

## Hands-on Exercise 1.4

Build the complete project structure.

### Part 1: Create the Marketing App

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

Update `apps/marketing/package.json`:

```json title="apps/marketing/package.json"
{
  "name": "marketing",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev --port 3000",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "^16.1.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "@acme/ui": "workspace:*"
  },
  "devDependencies": {
    "@types/node": "^22.10.0",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "typescript": "^5.7.0",
    "tailwindcss": "^3.4.0",
    "postcss": "^8.4.0",
    "autoprefixer": "^10.4.0"
  }
}
```

Create `apps/marketing/app/layout.tsx`:

```tsx title="apps/marketing/app/layout.tsx"
import type { Metadata } from "next";
import { Header } from "@acme/ui";
import "./globals.css";

export const metadata: Metadata = {
  title: "Acme Platform",
  description: "The everything app for your business",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className="min-h-screen bg-white flex flex-col">
        <Header />
        <main className="flex-1">{children}</main>
      </body>
    </html>
  );
}
```

Create `apps/marketing/app/page.tsx`:

```tsx title="apps/marketing/app/page.tsx"
export default function HomePage() {
  return (
    <div className="flex flex-col items-center justify-center min-h-[80vh] p-8">
      <h1 className="text-4xl font-bold mb-4">Welcome to Acme Platform</h1>
      <p className="text-gray-600 mb-8 text-center max-w-md">
        The everything app for your business.
      </p>
      <nav className="flex gap-4">
        <a href="/pricing" className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
          Pricing
        </a>
        <a href="/docs" className="px-4 py-2 border border-gray-300 rounded hover:bg-gray-50">
          Documentation
        </a>
      </nav>
    </div>
  );
}
```

Create `apps/marketing/app/pricing/page.tsx`:

```tsx title="apps/marketing/app/pricing/page.tsx"
export default function PricingPage() {
  return (
    <div className="max-w-4xl mx-auto p-8">
      <h1 className="text-3xl font-bold mb-8">Pricing</h1>
      <p className="text-gray-600">Simple, transparent pricing for your business.</p>
    </div>
  );
}
```

### Part 2: Create the Docs App

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

Update `apps/docs/package.json`:

```json title="apps/docs/package.json"
{
  "name": "docs",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev --port 3001",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "^16.1.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "@acme/ui": "workspace:*"
  },
  "devDependencies": {
    "@types/node": "^22.10.0",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "typescript": "^5.7.0",
    "tailwindcss": "^3.4.0",
    "postcss": "^8.4.0",
    "autoprefixer": "^10.4.0"
  }
}
```

Create the docs route structure. First, create the directory:

```bash
mkdir -p apps/docs/app/docs
```

Create `apps/docs/app/layout.tsx`:

```tsx title="apps/docs/app/layout.tsx"
import type { Metadata } from "next";
import { Header } from "@acme/ui";
import "./globals.css";

export const metadata: Metadata = {
  title: "Acme Docs",
  description: "Documentation for Acme Platform",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className="min-h-screen bg-white flex flex-col">
        <Header />
        <main className="flex-1">{children}</main>
      </body>
    </html>
  );
}
```

Create `apps/docs/app/docs/page.tsx`:

```tsx title="apps/docs/app/docs/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>
  );
}
```

### Part 3: Create the Dashboard App

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

Update `apps/dashboard/package.json`:

```json title="apps/dashboard/package.json"
{
  "name": "dashboard",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev --port 3002",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "^16.1.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "@acme/ui": "workspace:*"
  },
  "devDependencies": {
    "@types/node": "^22.10.0",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "typescript": "^5.7.0",
    "tailwindcss": "^3.4.0",
    "postcss": "^8.4.0",
    "autoprefixer": "^10.4.0"
  }
}
```

Create the dashboard route structure. First, create the directories:

```bash
mkdir -p apps/dashboard/app/app apps/dashboard/app/settings
```

Create `apps/dashboard/app/layout.tsx`:

```tsx title="apps/dashboard/app/layout.tsx"
import type { Metadata } from "next";
import { Header } from "@acme/ui";
import "./globals.css";

export const metadata: Metadata = {
  title: "Acme Dashboard",
  description: "Manage your Acme Platform",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className="min-h-screen bg-gray-50">
        <Header />
        <main className="p-8">{children}</main>
      </body>
    </html>
  );
}
```

Create `apps/dashboard/app/app/page.tsx`:

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

```tsx title="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>
  );
}
```

### Part 4: Create the Shared UI Package

Create the package directory:

```bash
cd ../packages
mkdir -p ui/src
```

Create `packages/ui/package.json`:

```json title="packages/ui/package.json"
{
  "name": "@acme/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    ".": {
      "types": "./src/index.ts",
      "default": "./src/index.ts"
    }
  },
  "scripts": {
    "lint": "eslint ."
  },
  "peerDependencies": {
    "react": "^19.0.0"
  },
  "devDependencies": {
    "@types/react": "^19.0.0",
    "typescript": "^5.7.0"
  }
}
```

Create `packages/ui/tsconfig.json`:

```json title="packages/ui/tsconfig.json"
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["dom", "dom.iterable", "ES2020"],
    "jsx": "react-jsx",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}
```

Create `packages/ui/src/header.tsx`:

```tsx title="packages/ui/src/header.tsx"
export function Header() {
  return (
    <header className="h-16 border-b bg-white px-6 flex items-center justify-between">
      <div className="flex items-center gap-8">
        <a href="/" className="font-bold text-xl">
          Acme
        </a>
        <nav className="hidden md:flex items-center gap-6">
          <a href="/" className="text-gray-600 hover:text-gray-900">
            Home
          </a>
          <a href="/docs" className="text-gray-600 hover:text-gray-900">
            Docs
          </a>
          <a href="/pricing" className="text-gray-600 hover:text-gray-900">
            Pricing
          </a>
        </nav>
      </div>
      <div className="flex items-center gap-4">
        <a
          href="/app"
          className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
        >
          Dashboard
        </a>
      </div>
    </header>
  );
}
```

Create `packages/ui/src/index.ts`:

```ts title="packages/ui/src/index.ts"
export { Header } from "./header";
```

### Part 5: Install All Dependencies

From the root:

```bash
cd ../..
pnpm install
```

## Try It

Run all three apps simultaneously:

```bash
pnpm dev
```

You should see Turborepo output showing all three apps starting:

```
• Packages in scope: @acme/ui, dashboard, docs, marketing
• Running dev in 3 packages

marketing:dev: ready - started server on 0.0.0.0:3000
docs:dev: ready - started server on 0.0.0.0:3001
dashboard:dev: ready - started server on 0.0.0.0:3002
```

Visit each app at its correct route:

- **<http://localhost:3000>** - Marketing (Home)
- **<http://localhost:3001/docs>** - Docs
- **<http://localhost:3002/app>** - Dashboard
- **<http://localhost:3002/settings>** - Settings

The same Header component should render in all three, with the current app highlighted.

## Commit

```bash
git add -A
git commit -m "feat: add marketing, docs, and dashboard apps with shared UI"
```

## Done-When

- [ ] Three Next.js apps created in `apps/`
- [ ] Shared UI package in `packages/ui/`
- [ ] Header component renders in all three apps
- [ ] `pnpm dev` runs all apps simultaneously
- [ ] Navigation links work (but route to wrong ports for now)

Navigation links won't work yet. Clicking "Docs" from localhost:3000 tries to load localhost:3000/docs, which doesn't exist. The apps are still separate. Section 2 stitches them together.

## What's Next

Three apps, one shared package, but no routing between them. Section 2 adds `microfrontends.json` and wires it all together.


---

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