home / skills / prowler-cloud / prowler / nextjs-15

nextjs-15 skill

/skills/nextjs-15

This skill helps you master Next.js 15 App Router patterns, including server components, actions, route handlers, and streaming with Suspense.

npx playbooks add skill prowler-cloud/prowler --skill nextjs-15

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.3 KB
---
name: nextjs-15
description: >
  Next.js 15 App Router patterns.
  Trigger: When working in Next.js App Router (app/), Server Components vs Client Components, Server Actions, Route Handlers, caching/revalidation, and streaming/Suspense.
license: Apache-2.0
metadata:
  author: prowler-cloud
  version: "1.0"
  scope: [root, ui]
  auto_invoke: "App Router / Server Actions"
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
---

## App Router File Conventions

```
app/
├── layout.tsx          # Root layout (required)
├── page.tsx            # Home page (/)
├── loading.tsx         # Loading UI (Suspense)
├── error.tsx           # Error boundary
├── not-found.tsx       # 404 page
├── (auth)/             # Route group (no URL impact)
│   ├── login/page.tsx  # /login
│   └── signup/page.tsx # /signup
├── api/
│   └── route.ts        # API handler
└── _components/        # Private folder (not routed)
```

## Server Components (Default)

```typescript
// No directive needed - async by default
export default async function Page() {
  const data = await db.query();
  return <Component data={data} />;
}
```

## Server Actions

```typescript
// app/actions.ts
"use server";

import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";

export async function createUser(formData: FormData) {
  const name = formData.get("name") as string;

  await db.users.create({ data: { name } });

  revalidatePath("/users");
  redirect("/users");
}

// Usage
<form action={createUser}>
  <input name="name" required />
  <button type="submit">Create</button>
</form>
```

## Data Fetching

```typescript
// Parallel
async function Page() {
  const [users, posts] = await Promise.all([
    getUsers(),
    getPosts(),
  ]);
  return <Dashboard users={users} posts={posts} />;
}

// Streaming with Suspense
<Suspense fallback={<Loading />}>
  <SlowComponent />
</Suspense>
```

## Route Handlers (API)

```typescript
// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest) {
  const users = await db.users.findMany();
  return NextResponse.json(users);
}

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

## Middleware

```typescript
// middleware.ts (root level)
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const token = request.cookies.get("token");

  if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  return NextResponse.next();
}

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

## Metadata

```typescript
// Static
export const metadata = {
  title: "My App",
  description: "Description",
};

// Dynamic
export async function generateMetadata({ params }) {
  const product = await getProduct(params.id);
  return { title: product.name };
}
```

## server-only Package

```typescript
import "server-only";

// This will error if imported in client component
export async function getSecretData() {
  return db.secrets.findMany();
}
```

Overview

This skill teaches practical App Router patterns for Next.js 15, covering file conventions, Server vs Client Components, Server Actions, Route Handlers, caching/revalidation, and streaming with Suspense. It focuses on common application structure, data fetching strategies, and integration points like middleware, metadata, and server-only modules. The guidance is concise and action-oriented to help build predictable, high-performance app/ routes.

How this skill works

The skill inspects the app/ folder layout and recommends where to place layout.tsx, page.tsx, loading.tsx, error.tsx, and not-found.tsx. It explains Server Components as the default async entry points, how to wire Server Actions for form handling with revalidatePath and redirect, and how to implement Route Handlers (app/api/route.ts) for REST-style endpoints. It also shows patterns for parallel data fetching, streaming with Suspense, middleware routing, metadata generation, and using server-only modules for secrets.

When to use it

  • When structuring a new Next.js 15 app using the App Router (app/).
  • When deciding between Server Components and Client Components for performance.
  • When you need built-in form actions without an API endpoint (Server Actions).
  • When exposing backend behavior via server-side REST handlers (Route Handlers).
  • When implementing streaming UI with Suspense and progressive rendering.

Best practices

  • Keep global layout and error boundaries at app/root to centralize UI concerns.
  • Default to Server Components; add 'use client' only for interactivity or hooks.
  • Use Server Actions for simple mutations and revalidatePath to update pages.
  • Parallelize independent fetches with Promise.all to reduce latency.
  • Protect sensitive code with 'server-only' imports and avoid using them in client files.
  • Use middleware matcher to scope auth redirects and reduce edge execution.

Example use cases

  • Build a dashboard where root layout provides auth checks and a protected /dashboard route via middleware.
  • Implement a user creation flow using a Server Action form that writes to the DB, revalidates /users, then redirects.
  • Expose backend data through app/api/users/route.ts for internal API consumption and third-party use.
  • Render a product page that streams slow reviews inside a Suspense boundary while the rest of the page loads.
  • Generate dynamic metadata per product by implementing generateMetadata that fetches product data server-side.

FAQ

When should I use a Client Component?

Use Client Components only for interactive UI that needs browser APIs, event handlers, or React state/hooks.

Can Server Actions call external APIs and revalidate pages?

Yes. Server Actions can perform mutations, call external services, use revalidatePath to update cached pages, and redirect after completion.

Where do I put non-routed components?

Place shared private UI in a non-routed folder like _components/ to avoid the App Router treating them as routes.