chore(skills): update Next.js guidance to version 16 (#11143)

This commit is contained in:
Alejandro Bailo
2026-05-12 19:05:13 +02:00
committed by GitHub
parent b2ed9ee221
commit 132e79df89
9 changed files with 184 additions and 162 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ Reusable patterns for common technologies:
|-------|-------------|
| `typescript` | Const types, flat interfaces, utility types |
| `react-19` | React 19 patterns, React Compiler |
| `nextjs-15` | App Router, Server Actions, streaming |
| `nextjs-16` | App Router, Server Actions, proxy.ts, streaming |
| `tailwind-4` | cn() utility, Tailwind 4 patterns |
| `playwright` | Page Object Model, selectors |
| `vitest` | Unit testing, React Testing Library |
-150
View File
@@ -1,150 +0,0 @@
---
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();
}
```
+160
View File
@@ -0,0 +1,160 @@
---
name: nextjs-16
description: >
Next.js 16 App Router patterns.
Trigger: When working in Next.js App Router (app/), Server Components vs Client Components, Server Actions, Route Handlers, proxy.ts, caching/revalidation, Cache Components, 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)
```
## Next.js 16 Notes
- Use `proxy.ts` for request-boundary logic. `middleware.ts` is deprecated in Next.js 16.
- `proxy.ts` runs on the Node.js runtime and cannot be configured for Edge.
- Keep `proxy.ts` matchers narrow. Exclude `api`, static files, and image assets unless the route explicitly needs proxy logic.
- Route Handlers in `app/api/**/route.ts` are the right fit for health checks, webhooks, backend-for-frontend endpoints, and server-only proxy calls.
## 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
"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");
}
```
## Data Fetching
```typescript
async function Page() {
const [users, posts] = await Promise.all([getUsers(), getPosts()]);
return <Dashboard users={users} posts={posts} />;
}
<Suspense fallback={<Loading />}>
<SlowComponent />
</Suspense>;
```
## Caching and Revalidation
```typescript
import { revalidatePath, revalidateTag } from "next/cache";
export async function refreshDashboard() {
"use server";
revalidatePath("/");
revalidateTag("dashboard");
}
```
- Use `revalidatePath` for route-level invalidation after mutations.
- Use `revalidateTag` when data fetches share a cache tag across routes.
- With Cache Components enabled, put `"use cache"` only in pure server-side cached functions. Do not cache auth, tenant-scoped, or per-user responses unless the cache key explicitly isolates them.
## Route Handlers (API)
```typescript
// app/api/users/route.ts
import { NextResponse } from "next/server";
export async function GET() {
const users = await db.users.findMany();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create({ data: body });
return NextResponse.json(user, { status: 201 });
}
```
## Proxy
```typescript
// proxy.ts (root level)
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function proxy(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
export const metadata = {
title: "My App",
description: "Description",
};
export async function generateMetadata() {
const product = await getProduct();
return { title: product.name };
}
```
## server-only Package
```typescript
import "server-only";
export async function getSecretData() {
return db.secrets.findMany();
}
```
+3 -3
View File
@@ -1,7 +1,7 @@
---
name: prowler-ui
description: >
Prowler UI-specific patterns. For generic patterns, see: typescript, react-19, nextjs-15, tailwind-4.
Prowler UI-specific patterns. For generic patterns, see: typescript, react-19, nextjs-16, tailwind-4.
Trigger: When working inside ui/ on Prowler-specific conventions (shadcn vs HeroUI legacy, folder placement, actions/adapters, shared types/hooks/lib).
license: Apache-2.0
metadata:
@@ -18,7 +18,7 @@ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
- `typescript` - Const types, flat interfaces
- `react-19` - No useMemo/useCallback, compiler
- `nextjs-15` - App Router, Server Actions
- `nextjs-16` - App Router, Server Actions
- `tailwind-4` - cn() utility, styling rules
- `zod-4` - Schema validation
- `zustand-5` - State management
@@ -28,7 +28,7 @@ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
## Tech Stack (Versions)
```
Next.js 15.5.9 | React 19.2.2 | Tailwind 4.1.13 | shadcn/ui
Next.js 16.2.3 | React 19.2.5 | Tailwind 4.1.18 | shadcn/ui
Zod 4.1.11 | React Hook Form 7.62.0 | Zustand 5.0.8
NextAuth 5.0.0-beta.30 | Recharts 2.15.4
HeroUI 2.8.4 (LEGACY - do not add new components)
+1 -1
View File
@@ -18,7 +18,7 @@ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
|-----------|-------|----------|
| SDK | Python 3.10+, Poetry | `prowler/` |
| API | Django 5.1, DRF, Celery | `api/` |
| UI | Next.js 15, React 19, Tailwind 4 | `ui/` |
| UI | Next.js 16, React 19, Tailwind 4 | `ui/` |
| MCP | FastMCP 2.13.1 | `mcp_server/` |
## Quick Commands
+1 -1
View File
@@ -2,7 +2,7 @@
name: react-19
description: >
React 19 patterns with React Compiler.
Trigger: When writing React 19 components/hooks in .tsx (React Compiler rules, hook patterns, refs as props). If using Next.js App Router/Server Actions, also use nextjs-15.
Trigger: When writing React 19 components/hooks in .tsx (React Compiler rules, hook patterns, refs as props). If using Next.js App Router/Server Actions, also use nextjs-16.
license: Apache-2.0
metadata:
author: prowler-cloud