---
title: "Add Docs App"
description: "Create apps/docs with Next.js, build API documentation page, use ui package components, and configure port 3002."
canonical_url: "https://vercel.com/academy/production-monorepos/add-docs-app"
md_url: "https://vercel.com/academy/production-monorepos/add-docs-app.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-12T03:16:36.316Z"
content_type: "lesson"
course: "production-monorepos"
course_title: "Production Monorepos with Turborepo"
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>

# Add Docs App

# Add docs app

You have 2 apps (web, app) proving monorepos work. But do they scale? Adding a third app proves all the patterns you've learned - shared packages, filtering, caching, CI - work effortlessly as the monorepo grows.

The docs app will use the same shared UI components, configs, and utils. You'll see how adding apps becomes trivial once the foundation is solid.

## Outcome

Create a third app (docs) that reuses all existing shared packages.

## Fast track

1. Create apps/docs with Next.js
2. Configure with shared config
3. Build docs page using shared components
4. Add to dev workflow

## Hands-on exercise 8.1

Create documentation app that showcases the monorepo's components.

**Requirements:**

1. Create apps/docs directory
2. Initialize Next.js app on port 3002
3. Configure shared packages (ui, config, utils)
4. Build API docs page listing components
5. Add environment variables
6. Add to turbo.json and package.json

**Implementation hints:**

- Use create-next-app or copy from existing app
- Reuse all shared packages (no new code needed!)
- Port 3002 to avoid conflicts
- Demonstrate component reuse

## Create docs app

### 1. Create directory and initialize

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

Create `apps/docs/package.json`:

```json title="apps/docs/package.json"
{
  "name": "@geniusgarage/docs",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev --port 3002",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "14.x",
    "react": "^18",
    "react-dom": "^18",
    "@geniusgarage/ui": "workspace:*"
  },
  "devDependencies": {
    "@geniusgarage/typescript-config": "workspace:*",
    "@geniusgarage/eslint-config": "workspace:*",
    "typescript": "^5",
    "tailwindcss": "^3",
    "autoprefixer": "^10",
    "postcss": "^8"
  }
}
```

Port 3002 avoids conflicts with web (3000) and app (3001).

### 2. Add TypeScript config

Create `apps/docs/tsconfig.json`:

```json title="apps/docs/tsconfig.json"
{
  "extends": "@geniusgarage/typescript-config/nextjs.json",
  "compilerOptions": {
    "plugins": [{ "name": "next" }]
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
```

### 3. Add Tailwind config

Create `apps/docs/tailwind.config.js`:

```js title="apps/docs/tailwind.config.js"
export default {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    '../../packages/ui/src/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

Create `apps/docs/app/globals.css`:

```css title="apps/docs/app/globals.css"
@tailwind base;
@tailwind components;
@tailwind utilities;
```

### 4. Add environment variables

Create `apps/docs/.env.example`:

```bash title="apps/docs/.env.example"
# Public app name
NEXT_PUBLIC_APP_NAME="GeniusGarage Docs"
```

Create `apps/docs/.env.local`:

```bash title="apps/docs/.env.local"
NEXT_PUBLIC_APP_NAME="GeniusGarage Docs"
```

## Build docs page

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

```tsx title="apps/docs/app/layout.tsx"
import './globals.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
```

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

```tsx title="apps/docs/app/page.tsx"
// TODO: Import Button, Card from '@geniusgarage/ui'
// TODO: Import env from '../env'

// TODO: Export default function Home() that renders:
//   - Container div with padding and max-width
//   - h1 with env.NEXT_PUBLIC_APP_NAME
//   - p describing "Component Library Documentation"
//   - Grid of Cards showcasing components:
//     - Button Card with examples
//     - Card Card showing the card itself
//     - CodeBlock Card (mentioned but not rendered here)
//   - Each Card should have a title and example usage
```

Solution

```tsx title="apps/docs/app/page.tsx"
import { Button } from '@geniusgarage/ui/button'
import { Card } from '@geniusgarage/ui/card'
import { env } from '../env'

export default function Home() {
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-4xl font-bold mb-4">{env.NEXT_PUBLIC_APP_NAME}</h1>
      <p className="text-gray-600 mb-8">
        Component library documentation and examples
      </p>

      <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
        <Card>
          <h2 className="text-2xl font-bold mb-4">Button</h2>
          <p className="mb-4">Interactive button component with variants</p>
          <div className="space-y-2">
            <Button>Primary Button</Button>
            <Button variant="secondary">Secondary Button</Button>
          </div>
        </Card>

        <Card>
          <h2 className="text-2xl font-bold mb-4">Card</h2>
          <p className="mb-4">Container component with shadow and padding</p>
          <p className="text-sm text-gray-600">You're looking at one!</p>
        </Card>

        <Card>
          <h2 className="text-2xl font-bold mb-4">CodeBlock</h2>
          <p className="mb-4">Syntax-highlighted code display</p>
          <p className="text-sm text-gray-600">
            Used in snippet manager app for displaying code
          </p>
        </Card>
      </div>
    </div>
  )
}
```

## Install dependencies

```bash
pnpm install
```

This links the workspace packages automatically!

## Try it

### 1. Run docs app

```bash
pnpm --filter @geniusgarage/docs dev
```

Output:

```
  ▲ Next.js 14.x.x
  - Local:        http://localhost:3002

Ready in 1.8s
```

Open <http://localhost:3002> - see docs with `Button` and `Card` examples!

### 2. Run all apps together

```bash
pnpm dev
```

Output:

```
@geniusgarage/web:dev: ready on http://localhost:3000
@geniusgarage/snippet-manager:dev: ready on http://localhost:3001
@geniusgarage/docs:dev: ready on http://localhost:3002
```

All 3 apps running in parallel!

### 3. Verify shared components work

Change `Button` in `packages/ui/src/button.tsx`:

```tsx
// Change primary color
primary: 'bg-green-500 text-white hover:bg-green-600',
```

Hot reload updates ALL 3 apps instantly! Shared components in action.

## Update root config

Add docs to root `package.json`:

```json title="package.json" {7}
{
  "name": "production-monorepos",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo build",
    "test": "turbo test",
    "lint": "turbo lint"
  }
}
```

Workspace glob already includes apps/docs!

## Verify in turbo

```bash
turbo build --dry
```

Output:

```
Tasks to Run
@geniusgarage/utils:build
@geniusgarage/ui:build
@geniusgarage/web:build
@geniusgarage/snippet-manager:build
@geniusgarage/docs:build  ← New app detected!

6 tasks
```

Turborepo automatically discovered the new app!

## How adding apps scales

**Before (2 apps):**

```
packages/ui → web, app
```

**After (3 apps):**

```
packages/ui → web, app, docs
```

**Effort to add docs:**

- Created package.json (reused dependencies)
- Created configs (reused shared configs)
- Built page (reused shared components)
- **Total new code: \~50 lines**

**What you didn't need:**

- New component library
- New TypeScript config
- New ESLint config
- New utilities
- New CI pipeline
- New caching setup

Everything just works!

## Commit

```bash
git add .
git commit -m "feat(docs): add documentation app"
```

## Done-when

Verify docs app works:

- [ ] Created apps/docs directory
- [ ] Added package.json with port 3002
- [ ] Created tsconfig.json extending shared config
- [ ] Added Tailwind config
- [ ] Created env.ts with validation
- [ ] Built page with Button and Card examples
- [ ] Installed dependencies with pnpm install
- [ ] Ran docs app on port 3002
- [ ] Ran all 3 apps with pnpm dev
- [ ] Changed shared component and saw hot reload in all apps
- [ ] Verified turbo build includes docs app
- [ ] Understood adding apps scales effortlessly

## What's Next

3 apps running locally, but are they deployed? Next lesson: **Deploy All Apps** - you'll deploy all 3 apps to Vercel as independent projects, proving monorepo deployment scales just as easily as local development.


---

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