nextjs-app-router

Next.js App Router patterns for production — server/client components, parallel routes, advanced middleware, RBAC three-tier, Redis caching, background jobs (BullMQ), data fetching, auth, deployment, CI/CD. Sources: Rambert (Advanced Next.js)...

Best use case

nextjs-app-router is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Next.js App Router patterns for production — server/client components, parallel routes, advanced middleware, RBAC three-tier, Redis caching, background jobs (BullMQ), data fetching, auth, deployment, CI/CD. Sources: Rambert (Advanced Next.js)...

Teams using nextjs-app-router should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/nextjs-app-router/SKILL.md --create-dirs "https://raw.githubusercontent.com/peterbamuhigire/skills-web-dev/main/skills/frontend-ux/nextjs-app-router/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/nextjs-app-router/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How nextjs-app-router Compares

Feature / Agentnextjs-app-routerStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Next.js App Router patterns for production — server/client components, parallel routes, advanced middleware, RBAC three-tier, Redis caching, background jobs (BullMQ), data fetching, auth, deployment, CI/CD. Sources: Rambert (Advanced Next.js)...

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Next.js App Router Patterns
Acknowledgement: Shared by Peter Bamuhigire, techguypeter.com, +256 784 464178.

<!-- dual-compat-start -->
## Use When

- Next.js App Router patterns for production — server/client components, parallel routes, advanced middleware, RBAC three-tier, Redis caching, background jobs (BullMQ), data fetching, auth, deployment, CI/CD. Sources: Rambert (Advanced Next.js)...
- The task needs reusable judgment, domain constraints, or a proven workflow rather than ad hoc advice.

## Do Not Use When

- The task is unrelated to `nextjs-app-router` or would be better handled by a more specific companion skill.
- The request only needs a trivial answer and none of this skill's constraints or references materially help.

## Required Inputs

- Gather relevant project context, constraints, and the concrete problem to solve.
- Confirm the desired deliverable: design, code, review, migration plan, audit, or documentation.

## Workflow

- Read this `SKILL.md` first, then load only the referenced deep-dive files that are necessary for the task.
- Apply the ordered guidance, checklists, and decision rules in this skill instead of cherry-picking isolated snippets.
- Produce the deliverable with assumptions, risks, and follow-up work made explicit when they matter.

## Quality Standards

- Keep outputs execution-oriented, concise, and aligned with the repository's baseline engineering standards.
- Preserve compatibility with existing project conventions unless the skill explicitly requires a stronger standard.
- Prefer deterministic, reviewable steps over vague advice or tool-specific magic.

## Anti-Patterns

- Treating examples as copy-paste truth without checking fit, constraints, or failure modes.
- Loading every reference file by default instead of using progressive disclosure.

## Outputs

- A concrete result that fits the task: implementation guidance, review findings, architecture decisions, templates, or generated artifacts.
- Clear assumptions, tradeoffs, or unresolved gaps when the task cannot be completed from available context alone.
- References used, companion skills, or follow-up actions when they materially improve execution.

## Evidence Produced

| Category | Artifact | Format | Example |
|----------|----------|--------|---------|
| Correctness | Route + middleware test plan | Markdown doc covering server/client component boundaries, parallel routes, and middleware | `docs/web/nextjs-route-tests.md` |
| Security | RBAC three-tier configuration note | Markdown doc covering middleware / server action / data-access guards | `docs/web/nextjs-rbac.md` |

## References

- Use the links and companion skills already referenced in this file when deeper context is needed.
<!-- dual-compat-end -->
## Project Setup

```bash
npx create-next-app@latest my-app \
  --typescript --tailwind --eslint --app --src-dir
```

### Folder Structure (App Router)

```
app/
├── layout.tsx          # Root layout (required)
├── page.tsx            # Home route /
├── (marketing)/        # Route group — no URL segment
├── @notifications/     # Parallel route slot
│   └── page.tsx
├── dashboard/
│   ├── layout.tsx      # Nested layout
│   ├── page.tsx
│   ├── loading.tsx     # Suspense fallback
│   ├── error.tsx       # Error boundary ('use client')
│   └── not-found.tsx
└── api/users/route.ts  # GET/POST /api/users
```

---

## Server vs Client Components

```tsx
// Server component (default) — async, zero JS to client
export default async function UsersPage() {
  const users = await fetch('https://api.example.com/users', {
    cache: 'force-cache',        // static
    // next: { revalidate: 60 } // ISR
    // cache: 'no-store'        // SSR
  }).then(r => r.json());
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

// Client component — needs interactivity, browser APIs
'use client';
export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}
```

**Rule:** Push `'use client'` as far down the tree as possible.

---

## Routing

| File | Route |
|------|-------|
| `app/page.tsx` | `/` |
| `app/blog/[slug]/page.tsx` | `/blog/:slug` |
| `app/shop/[...slug]/page.tsx` | `/shop/*` catch-all |
| `app/(auth)/login/page.tsx` | `/login` (route group) |
| `app/@modal/page.tsx` | Parallel slot |

### Dynamic Routes + generateStaticParams

```tsx
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await fetchPost(params.slug);
  return <article><h1>{post.title}</h1></article>;
}
export async function generateStaticParams() {
  const posts = await fetchAllPosts();
  return posts.map(p => ({ slug: p.slug }));
}
```

### Parallel Routes (@slot)

```tsx
// app/support/layout.tsx — load @tickets and @chat independently
export default function SupportLayout({ tickets, chat }: {
  tickets: React.ReactNode; chat: React.ReactNode;
}) {
  return (
    <div className="grid grid-cols-2 gap-4">
      <aside>{tickets}</aside>
      <main>{chat}</main>
    </div>
  );
}
// Directories: app/support/@tickets/page.tsx  app/support/@chat/page.tsx
// Use case: dashboards, live feeds, multi-pane UIs loading at different speeds
```

---

## Data Fetching

### ISR Revalidation Guidelines

| Content Type | Revalidate | Pattern |
|---|---|---|
| Blog / docs | 60s | `next: { revalidate: 60 }` |
| News feed | 10s | `next: { revalidate: 10 }` |
| Products / pricing | 300s | `next: { revalidate: 300 }` |
| Static marketing | 86400s | `cache: 'force-cache'` |
| User-specific | Dynamic | `cache: 'no-store'` |
| Real-time (stock) | N/A | SSR or WebSocket |

### Parallel Fetch + React cache()

```tsx
// Parallel fetching
const [user, posts] = await Promise.all([
  fetch('/api/user').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
]);

// Deduplication for POST/GraphQL requests
import { cache } from 'react';
export const getUser = cache(async (id: string) => db.user.findUnique({ where: { id } }));
```

---

## Route Handlers (API Routes)

```ts
// app/api/users/route.ts
export async function GET() {
  return NextResponse.json(await db.user.findMany());
}
export async function POST(request: NextRequest) {
  const body = await request.json();
  return NextResponse.json(await db.user.create({ data: body }), { status: 201 });
}
```

---

## Server Actions

```tsx
'use server';
import { revalidatePath } from 'next/cache';
export async function createTodo(formData: FormData) {
  const title = formData.get('title') as string;
  await db.todo.create({ data: { title } });
  revalidatePath('/todos');
}

// Use directly in form — no API route needed
<form action={createTodo}><input name="title" /><button>Add</button></form>
```

### Server Actions vs API Routes

| Use | When |
|---|---|
| Server Action | Form submissions, mutations from UI, same-app data changes |
| API Route | Public APIs, webhooks, third-party consumers, complex error handling |

---

## Middleware

```ts
// middleware.ts — project root
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const { pathname, url } = request.nextUrl;

  // Auth redirect
  const token = request.cookies.get('token')?.value;
  if (!token && pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', url));
  }

  // IP blocking
  const ip = request.ip || request.headers.get('x-forwarded-for') || '';
  const blocked = ['192.168.1.100'];
  if (blocked.includes(ip)) return new NextResponse('Forbidden', { status: 403 });

  // Geo-routing
  const country = request.geo?.country;
  if (country === 'FR' && !pathname.startsWith('/fr')) {
    return NextResponse.rewrite(new URL('/fr' + pathname, url));
  }

  // Custom headers
  const res = NextResponse.next();
  res.headers.set('X-Request-ID', crypto.randomUUID());
  return res;
}

export const config = { matcher: ['/dashboard/:path*', '/api/:path*'] };
```

---

## RBAC — Three-Tier Protection

```ts
// Tier 1: Middleware (route-level gating)
if (pathname.startsWith('/admin')) {
  const role = request.cookies.get('role')?.value;
  if (role !== 'admin') return NextResponse.redirect(new URL('/unauthorized', url));
}

// Tier 2: Server component (page-level protection)
import { auth } from '@/auth';
import { redirect } from 'next/navigation';
export default async function AdminPage() {
  const session = await auth();
  if (!session || session.user.role !== 'admin') redirect('/unauthorized');
  return <div>Admin Panel</div>;
}

// Tier 3: API route (data-level protection)
export async function GET(req: NextRequest) {
  const session = await getServerSession(authOptions);
  if (!session || session.user.role !== 'admin')
    return Response.json({ error: 'Unauthorized' }, { status: 403 });
  return Response.json(await db.adminData.findMany());
}
```

---

## Redis Caching

```ts
// lib/redis.ts
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!);
export default redis;

// API route with cache-aside pattern
export async function GET() {
  const cached = await redis.get('users');
  if (cached) return Response.json(JSON.parse(cached));

  const users = await db.user.findMany();
  await redis.set('users', JSON.stringify(users), 'EX', 600); // 10 min
  return Response.json(users);
}

// Invalidate on mutation
export async function POST(request: NextRequest) {
  const user = await db.user.create({ data: await request.json() });
  await redis.del('users');
  return Response.json(user, { status: 201 });
}
```

---

## Background Jobs (BullMQ)

```ts
// lib/queue.ts
import { Queue, Worker } from 'bullmq';
import Redis from 'ioredis';
const connection = new Redis(process.env.REDIS_URL!);
export const emailQueue = new Queue('email', { connection });

// Worker (separate process or dedicated route)
new Worker('email', async (job) => {
  const { to, subject, body } = job.data;
  await sendEmail(to, subject, body);
}, { connection });

// Enqueue from API route
export async function POST(req: NextRequest) {
  const { email } = await req.json();
  await emailQueue.add('welcome', { to: email, subject: 'Welcome!' });
  return Response.json({ queued: true });
}
```

---

## Authentication (NextAuth v5)

```ts
// auth.ts
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [GitHub({ clientId: process.env.GITHUB_ID!, clientSecret: process.env.GITHUB_SECRET! })],
  callbacks: {
    jwt({ token, user }) { if (user) token.role = user.role; return token; },
    session({ session, token }) { session.user.role = token.role as string; return session; },
  },
});
```

---

## Database (Prisma Singleton)

```ts
// lib/prisma.ts — prevents connection exhaustion in dev HMR
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const db = globalForPrisma.prisma || new PrismaClient({ log: ['query'] });
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
```

### Database Selection

| Database | Use When |
|---|---|
| PostgreSQL + Prisma | Relational data, transactions, production default |
| MongoDB + Mongoose | Document/flexible schemas, content management |
| Firebase Firestore | Real-time sync, serverless, mobile-first |

---

## CI/CD (GitHub Actions)

```yaml
name: CI/CD
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with: { node-version: '20.x' }
      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build
  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm install -g vercel && vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
```

---

## Deployment

| Platform | Best For |
|---|---|
| **Vercel** | Zero-config, global CDN, Edge Network, native Next.js |
| **Railway** | Simple self-hosted, easy DB provisioning |
| **AWS Amplify** | Full AWS ecosystem, enterprise |
| **Docker/Self-hosted** | Full control, no vendor lock-in |

```dockerfile
# next.config.js: module.exports = { output: 'standalone' }
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
CMD ["node", "server.js"]
```

---

## Built-in Components

```tsx
import Image from 'next/image';
import Link from 'next/link';

<Image src="/hero.jpg" alt="Hero" width={800} height={600} priority />
<Link href="/dashboard">Dashboard</Link>
<Link href={`/blog/${slug}`} prefetch={false}>Post</Link>
```

---

## Anti-Patterns

- Do NOT `'use client'` every component — server by default
- Do NOT use `getServerSideProps`/`getStaticProps` in App Router
- Do NOT store secrets in `NEXT_PUBLIC_` vars
- Do NOT `useEffect` for data — fetch in server components
- Do NOT create separate Express servers — use Route Handlers
- Do NOT await fetches sequentially — use `Promise.all`
- Do NOT skip `loading.tsx` — every dynamic route needs a Suspense boundary

---

*Sources: Rambert — Advanced Next.js for Everyone (2024); Kim — The Next.js Handbook (2023); Jain — Modern Web Applications with Next.js (2024); Krause — The Complete Developer (2024)*

Related Skills

web-app-security-audit

8
from peterbamuhigire/skills-web-dev

Use when auditing a PHP/JavaScript/HTML web application for security vulnerabilities. Covers configuration, authentication, authorization, input validation, XSS, API security, HTTP headers, and dependency scanning. Produces a severity-rated audit...

vibe-security-skill

8
from peterbamuhigire/skills-web-dev

Use when designing or reviewing security for a web application, API, or multi-tenant SaaS — produces threat model, abuse case list, auth/authz matrix, and secret handling plan; covers OWASP Top 10 2025 and the AI-code-generation blind spots. Neighbours — api-design-first owns auth model fields, deployment-release-engineering owns secret rotation choreography, ai-security and llm-security own model-specific threats.

network-security

8
from peterbamuhigire/skills-web-dev

Use when designing, hardening, or auditing network-layer security for self-managed Debian/Ubuntu SaaS infrastructure — firewalls (nftables/UFW), WAF (ModSecurity + OWASP CRS), VPN (WireGuard, OpenVPN, IPsec), TLS/PKI ops, IDS/IPS (Suricata, Fail2ban), zero-trust, SSH hardening, DDoS mitigation, DNS security. Complements web-app-security-audit (app layer) and cicd-devsecops (secrets/CI).

linux-security-hardening

8
from peterbamuhigire/skills-web-dev

Use when hardening a Debian/Ubuntu server — user/group/sudo hardening, file permission audits, PAM password policy + MFA, AppArmor mandatory access control, auditd system call logging, kernel sysctl hardening, file integrity monitoring (AIDE), rootkit detection (rkhunter/chkrootkit), unattended security patching, GRUB + UEFI + LUKS boot security, and CIS benchmark compliance.

dpia-generator

8
from peterbamuhigire/skills-web-dev

Generate a Data Protection Impact Assessment (DPIA), Uganda DPPA 2019-compliant. Use when producing or reviewing a data protection impact assessment, a privacy impact assessment, when uganda-dppa-compliance flags [DPIA-REQUIRED], or when processing large-scale or sensitive personal data for a new feature.

code-safety-scanner

8
from peterbamuhigire/skills-web-dev

Scan any codebase for 14 critical safety issues across security vulnerabilities, server stability (500 errors), and payment misconfigurations. Use when auditing code before deployment, reviewing AI-generated code for production readiness, or...

world-class-engineering

8
from peterbamuhigire/skills-web-dev

Use when designing, building, reviewing, or upgrading production software systems that must be secure, performant, maintainable, scalable, and user-centered. Apply before writing specs, code, architecture, APIs, databases, mobile apps, SaaS platforms, or ERP systems.

update-Codex-documentation

8
from peterbamuhigire/skills-web-dev

Update project documentation files (README.md, PROJECT_BRIEF.md, TECH_STACK.md, ARCHITECTURE.md, docs/API.md, docs/DATABASE.md, AGENTS.md, docs/plans/NEXT_FEATURES.md) when significant changes occur. MANDATORY at end of each work session to...

skill-writing

8
from peterbamuhigire/skills-web-dev

Use when creating or upgrading skills in this repository. Covers repository-specific frontmatter rules, progressive disclosure, reference-file strategy, validation, and the quality bar required for production-grade engineering skills.

skill-safety-audit

8
from peterbamuhigire/skills-web-dev

Scan new or updated skills for unsafe or malicious instructions (unknown tools, external installers, credential harvesting) before accepting them into the repository.

skill-composition-standards

8
from peterbamuhigire/skills-web-dev

Use when authoring a new skill, normalising an older skill, or reviewing a skill PR — defines the repository-wide house style (frontmatter, decision rules, anti-patterns, references), the output contracts each baseline-skill type must produce, and the input contracts each specialist skill must declare. This is the enforcement spine that makes the repository compose as a system, not a library of linked documents.

sdlc-documentation

8
from peterbamuhigire/skills-web-dev

Use when producing, reviewing, or consolidating SDLC documentation across planning, requirements, design, testing, deployment, user rollout, post-deployment, and maintenance phases. Load absorbed SDLC phase references as needed.