nextjs-16
Skill1 starsChanged 9 months agoApache-2.0View on GitHub
Build and review Next.js 16 apps: Turbopack, Cache Components, proxy.ts (renamed from middleware.ts), Server Actions, React 19.2 View Transitions, streaming SSR, ISR, error boundaries, and App Router patterns. Use when writing or migrating Next.js 16 code, working in an app/ directory, editing next.config, page.tsx, layout.tsx, route.ts or proxy.ts, or when the user asks about caching, server components, or server actions in Next.js.
- Sends data out
3 other projects use a version of this.
Adopted by
Most recently taken up by
- medy-gribkov/arcanaword for word, first seen Nov 1, 2026
- prowler-cloud/prowlertheir own version, first seen Oct 8, 2026
- anasahmed07/doittheir own version, first seen Jan 2, 2026
Other versions3 projects use a version of this.
Word for word · 1
- medy-gribkov/arcana★ 1100% same
Edited · 0
None found.
Wrote their own version · 2
- prowler-cloud/prowler★ 15ksame name
- anasahmed07/doit★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository21 other files it gives its agents.
- accessibility-wcagSkill
- api-designSkill
- bugbounty-reportSkill
- ci-cd-pipelinesSkill
- codebase-analysisSkill
- database-designSkill
- design-docSkill
- docker-kubernetesSkill
- git-workflowSkill
- go-devSkill
- i18n-localizationSkill
- llm-integrationSkill
- oauth-authSkill
- performance-optimizationSkill
- python-best-practicesSkill
- scraping-automationSkill
- security-reviewSkill
- seo-metaSkill
- tailwind-cssSkill
- testingSkill
- ts-devSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 0 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Cache Components over implicit caching: Use "use cache" directive explicitly.
- Very specific: line 18
proxy.ts over middleware.ts: Use proxy.ts for request interception (Node.js runtime).
- Very specific: line 462
Add layout.tsx: if shared UI needed across child routes
- Very specific: line 468
Add proxy.ts: if auth/redirects needed (not middleware.ts)
- Very specific: line 494
Use proxy.ts instead of middleware.ts.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Next.js 16 Development Skill
You are a Next.js 16 expert. Build modern web applications using Turbopack, Cache Components, App Router, React Server Components, and streaming SSR.
Core Principles
- Server Components by default. Client Components only when needed (interactivity, hooks, browser APIs).
- Streaming and Suspense. Load UI progressively, never block entire page.
- Cache Components over implicit caching. Use
"use cache"directive explicitly. All dynamic code runs at request time by default. - Type safety. TypeScript strict mode. All params/searchParams are async (
await params). - proxy.ts over middleware.ts. Use
proxy.tsfor request interception (Node.js runtime).middleware.tsis deprecated.
App Router Structure
BAD - Pages Router patterns in new code (legacy; still supported, but App Router is where Next.js 16 features live):
// pages/api/users.ts - legacy Pages Router API route
export default function handler(req, res) {
res.json({ users: [] })
}GOOD - App Router with colocation:
// app/dashboard/page.tsx
export default function DashboardPage() {
return <div>Dashboard</div>
}
// app/api/users/route.ts
export async function GET() {
return Response.json({ users: [] })
}
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return <div className="dashboard-wrapper">{children}</div>
}Cache Components (New in 16)
BAD - Old caching model (experimental.ppr is removed in 16; cacheComponents replaces it):
// experimental.ppr flag - REMOVED
// next.config: { experimental: { ppr: true } } - REMOVED
export default async function PostsPage() {
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache' // Per-fetch caching flags; Cache Components uses "use cache" instead
})
const posts = await res.json()
return <PostList posts={posts} />
}GOOD - Explicit Cache Components with "use cache":
// next.config.ts
const nextConfig = {
cacheComponents: true,
}
export default nextConfig
// app/posts/page.tsx
"use cache"
export default async function PostsPage() {
const posts = await db.posts.findMany()
return <PostList posts={posts} />
}GOOD - Cache at function level:
async function getProducts() {
"use cache"
return await db.products.findMany()
}
export default async function ProductsPage() {
const products = await getProducts()
return <ProductGrid products={products} />
}Proxy (Replaces Middleware)
BAD - Using deprecated middleware.ts:
// middleware.ts - DEPRECATED, will be removed
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}GOOD - Using proxy.ts (Node.js runtime):
// proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export default function proxy(request: NextRequest) {
const token = request.cookies.get('token')?.value
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/admin/:path*']
}Server Components vs Client Components
BAD - Using Client Component unnecessarily:
'use client'
export default function UserList({ users }: { users: User[] }) {
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}GOOD - Server Component with async params (required in 16):
// app/users/page.tsx
export default async function UsersPage() {
const users = await db.users.findMany()
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}
// app/users/[id]/page.tsx - params are async in 16
export default async function UserPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const user = await db.users.findUnique({ where: { id } })
return <div>{user.name}</div>
}Streaming SSR with Suspense
BAD - Blocking entire page load:
export default async function Dashboard() {
const user = await fetchUser()
const posts = await fetchPosts()
const comments = await fetchComments()
return (
<div>
<UserProfile user={user} />
<Posts posts={posts} />
<Comments comments={comments} />
</div>
)
}GOOD - Stream components independently:
import { Suspense } from 'react'
export default function Dashboard() {
return (
<div>
<Suspense fallback={<UserSkeleton />}>
<UserProfile />
</Suspense>
<Suspense fallback={<PostsSkeleton />}>
<Posts />
</Suspense>
<Suspense fallback={<CommentsSkeleton />}>
<Comments />
</Suspense>
</div>
)
}
async function UserProfile() {
const user = await fetchUser()
return <div>{user.name}</div>
}Server Actions
BAD - Client-side fetch to API route:
'use client'
export default function CreatePostForm() {
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
await fetch('/api/posts', { method: 'POST', body: JSON.stringify({}) })
}
return <form onSubmit={handleSubmit}>...</form>
}GOOD - Server Actions with progressive enhancement:
// app/posts/actions.ts
'use server'
import { revalidateTag, updateTag } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
await db.posts.create({ data: { title } })
revalidateTag('posts', 'max') // SWR with cacheLife profile (required in 16)
return { success: true }
}
// For read-your-writes (user sees changes instantly):
export async function updatePost(id: string, formData: FormData) {
await db.posts.update({ where: { id }, data: { title: formData.get('title') } })
updateTag(`post-${id}`) // New in 16: immediate cache expiry
}GOOD - Server Actions with useActionState:
'use client'
import { useActionState } from 'react'
import { updateProfile } from './actions'
export default function ProfileForm() {
const [state, formAction, isPending] = useActionState(updateProfile, null)
return (
<form action={formAction}>
<input name="name" />
{state?.error && <p className="error">{state.error}</p>}
<button type="submit" disabled={isPending}>
{isPending ? 'Saving...' : 'Update'}
</button>
</form>
)
}Caching APIs (Updated in 16)
BAD - Old revalidateTag with single argument (deprecated):
revalidateTag('posts') // Deprecated single-arg formGOOD - New caching APIs:
import { revalidateTag, updateTag, refresh } from 'next/cache'
// SWR revalidation with cacheLife profile
revalidateTag('posts', 'max') // Background revalidation, serve stale
revalidateTag('news', 'hours') // Revalidate within hours profile
revalidateTag('data', { expire: 3600 }) // Custom TTL
// Read-your-writes (Server Actions only)
updateTag('user-profile') // Expire + immediate fresh read
// Refresh uncached data (Server Actions only)
refresh() // Refresh dynamic data without touching cacheReact 19.2 Features
View Transitions:
'use client'
import { ViewTransition } from 'react'
export default function PhotoGrid({ photos }) {
return (
<div className="grid">
{photos.map(photo => (
<ViewTransition key={photo.id} name={`photo-${photo.id}`}>
<Link href={`/photos/${photo.id}`}>
<img src={photo.url} alt={photo.title} />
</Link>
</ViewTransition>
))}
</div>
)
}Activity (background rendering):
'use client'
import { Activity } from 'react'
export default function TabPanel({ activeTab, tabs }) {
return (
<div>
{tabs.map(tab => (
<Activity key={tab.id} mode={tab.id === activeTab ? 'visible' : 'hidden'}>
<TabContent tab={tab} />
</Activity>
))}
</div>
)
}Error Boundaries
// app/posts/[id]/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<p>{error.message}</p>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
// app/posts/[id]/loading.tsx
export default function Loading() {
return <div className="skeleton">Loading post...</div>
}
// app/posts/[id]/not-found.tsx
export default function NotFound() {
return <div>Post not found</div>
}Parallel Routes
BAD - Conditional rendering in one component:
export default function Dashboard({ searchParams }) {
return (
<div>
<Sidebar />
{searchParams.modal === 'settings' && <SettingsModal />}
</div>
)
}GOOD - Parallel routes with slots (all slots need default.tsx in 16):
app/dashboard/
├── @modal/
│ ├── settings/page.tsx
│ ├── profile/page.tsx
│ └── default.tsx # REQUIRED in 16
├── @sidebar/
│ ├── page.tsx
│ └── default.tsx # REQUIRED in 16
├── layout.tsx
└── page.tsx// app/dashboard/layout.tsx
export default function DashboardLayout({
children, modal, sidebar,
}: {
children: React.ReactNode
modal: React.ReactNode
sidebar: React.ReactNode
}) {
return (
<div className="dashboard">
<aside>{sidebar}</aside>
<main>{children}</main>
{modal}
</div>
)
}
// app/dashboard/@modal/default.tsx - REQUIRED
import { notFound } from 'next/navigation'
export default function Default() {
return null // or notFound()
}Route Handlers
// app/api/posts/route.ts
import { NextRequest } from 'next/server'
import { z } from 'zod'
const postSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
})
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const validated = postSchema.parse(body)
const post = await db.posts.create({ data: validated })
return Response.json({ post }, { status: 201 })
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json({ error: error.issues }, { status: 400 })
}
return Response.json({ error: 'Internal error' }, { status: 500 })
}
}
// Async params in route handlers (required in 16)
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
const post = await db.posts.findUnique({ where: { id } })
if (!post) return Response.json({ error: 'Not found' }, { status: 404 })
return Response.json({ post })
}Procedural Workflow: New Feature
- Create route segment in
app/withpage.tsx - Add layout.tsx if shared UI needed across child routes
- Use Server Component by default, fetch data directly
- Add
"use cache"directive for pages/components that benefit from caching - Add Suspense boundaries for slow data fetching
- Create loading.tsx and error.tsx for each route
- Add Server Actions in
actions.tsfor mutations (useupdateTagfor read-your-writes) - Add proxy.ts if auth/redirects needed (not middleware.ts)
- Add default.tsx to all parallel route slots
- Enable Turbopack FS caching for large projects (
turbopackFileSystemCacheForDev)
Turbopack Configuration
// next.config.ts
const nextConfig = {
// Turbopack is default in 16, no config needed
// To use webpack instead, pass the flag: next dev --webpack, next build --webpack
// Enable filesystem caching (beta) for faster restarts
experimental: {
turbopackFileSystemCacheForDev: true,
},
// React Compiler (stable in 16, opt-in)
reactCompiler: true,
// Cache Components
cacheComponents: true,
}
export default nextConfigUse proxy.ts instead of middleware.ts. All params are async. All parallel slots need default.tsx. Use "use cache" instead of experimental.ppr. Turbopack is default.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

