nextjs-app-router

安装量: 214
排名: #4087

安装

npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill nextjs-app-router
Next.js App Router (Next.js 16+)
Build modern React applications using Next.js 16+ with App Router architecture.
Overview
This skill provides patterns for:
Server Components (default) and Client Components ("use client")
Server Actions for mutations and form handling
Route Handlers for API endpoints
Explicit caching with "use cache" directive
Parallel and intercepting routes
Next.js 16 async APIs and proxy.ts
When to Use
Activate when user requests involve:
"Create a Next.js 16 project", "Set up App Router"
"Server Component", "Client Component", "use client"
"Server Action", "form submission", "mutation"
"Route Handler", "API endpoint", "route.ts"
"use cache", "cacheLife", "cacheTag", "revalidation"
"parallel routes", "@slot", "intercepting routes"
"proxy.ts", "migrate from middleware.ts"
"layout.tsx", "page.tsx", "loading.tsx", "error.tsx", "not-found.tsx"
"generateMetadata", "next/image", "next/font"
Quick Reference
File Conventions
File
Purpose
page.tsx
Route page component
layout.tsx
Shared layout wrapper
loading.tsx
Suspense loading UI
error.tsx
Error boundary
not-found.tsx
404 page
template.tsx
Re-mounting layout
route.ts
API Route Handler
default.tsx
Parallel route fallback
proxy.ts
Routing boundary (Next.js 16)
Directives
Directive
Purpose
"use server"
Mark Server Action functions
"use client"
Mark Client Component boundary
"use cache"
Enable explicit caching (Next.js 16)
Instructions
Create New Project
npx create-next-app@latest my-app
--typescript
--tailwind
--app
--turbopack
Implement Server Component
Server Components are the default in App Router.
// app/users/page.tsx
async
function
getUsers
(
)
{
const
apiUrl
=
process
.
env
.
API_URL
;
const
res
=
await
fetch
(
`
${
apiUrl
}
/users
`
)
;
return
res
.
json
(
)
;
}
export
default
async
function
UsersPage
(
)
{
const
users
=
await
getUsers
(
)
;
return
(
<
main
>
{
users
.
map
(
user
=>
<
UserCard
key
=
{
user
.
id
}
user
=
{
user
}
/>
)
}
</
main
>
)
;
}
Implement Client Component
Add
"use client"
when using hooks, browser APIs, or event handlers.
"use client"
;
import
{
useState
}
from
"react"
;
export
default
function
Counter
(
)
{
const
[
count
,
setCount
]
=
useState
(
0
)
;
return
(
<
button
onClick
=
{
(
)
=>
setCount
(
c
=>
c
+
1
)
}
>
Count:
{
count
}
</
button
>
)
;
}
Create Server Action
Define actions in separate files with "use server" directive.
// app/actions.ts
"use server"
;
import
{
revalidatePath
}
from
"next/cache"
;
export
async
function
createUser
(
formData
:
FormData
)
{
const
name
=
formData
.
get
(
"name"
)
as
string
;
const
email
=
formData
.
get
(
"email"
)
as
string
;
await
db
.
user
.
create
(
{
data
:
{
name
,
email
}
}
)
;
revalidatePath
(
"/users"
)
;
}
Use with forms in Client Components:
"use client"
;
import
{
useActionState
}
from
"react"
;
import
{
createUser
}
from
"./actions"
;
export
default
function
UserForm
(
)
{
const
[
state
,
formAction
,
pending
]
=
useActionState
(
createUser
,
{
}
)
;
return
(
<
form
action
=
{
formAction
}
>
<
input
name
=
"
name
"
/>
<
input
name
=
"
email
"
type
=
"
email
"
/>
<
button
type
=
"
submit
"
disabled
=
{
pending
}
>
{
pending
?
"Creating..."
:
"Create"
}
</
button
>
</
form
>
)
;
}
See
references/server-actions.md
for validation with Zod, optimistic updates, and advanced patterns.
Configure Caching
Use "use cache" directive for explicit caching (Next.js 16+).
"use cache"
;
import
{
cacheLife
,
cacheTag
}
from
"next/cache"
;
export
default
async
function
ProductPage
(
{
params
,
}
:
{
params
:
Promise
<
{
id
:
string
}
>
;
}
)
{
const
{
id
}
=
await
params
;
cacheTag
(
`
product-
${
id
}
`
)
;
cacheLife
(
"hours"
)
;
const
product
=
await
fetchProduct
(
id
)
;
return
<
ProductDetail
product
=
{
product
}
/>
;
}
See
references/caching-strategies.md
for cache profiles, on-demand revalidation, and advanced caching patterns.
Create Route Handler
Implement API endpoints using Route Handlers.
// app/api/users/route.ts
import
{
NextRequest
,
NextResponse
}
from
"next/server"
;
export
async
function
GET
(
request
:
NextRequest
)
{
const
users
=
await
db
.
user
.
findMany
(
)
;
return
NextResponse
.
json
(
users
)
;
}
export
async
function
POST
(
request
:
NextRequest
)
{
const
body
=
await
request
.
json
(
)
;
const
user
=
await
db
.
user
.
create
(
{
data
:
body
}
)
;
return
NextResponse
.
json
(
user
,
{
status
:
201
}
)
;
}
Dynamic segments use
[param]
:
// app/api/users/[id]/route.ts
interface
RouteParams
{
params
:
Promise
<
{
id
:
string
}
>
;
}
export
async
function
GET
(
request
:
NextRequest
,
{
params
}
:
RouteParams
)
{
const
{
id
}
=
await
params
;
const
user
=
await
db
.
user
.
findUnique
(
{
where
:
{
id
}
}
)
;
if
(
!
user
)
{
return
NextResponse
.
json
(
{
error
:
"Not found"
}
,
{
status
:
404
}
)
;
}
return
NextResponse
.
json
(
user
)
;
}
Handle Next.js 16 Async APIs
All Next.js APIs are async in version 16.
import
{
cookies
,
headers
,
draftMode
}
from
"next/headers"
;
export
default
async
function
Page
(
)
{
const
cookieStore
=
await
cookies
(
)
;
const
headersList
=
await
headers
(
)
;
const
{
isEnabled
}
=
await
draftMode
(
)
;
const
session
=
cookieStore
.
get
(
"session"
)
?.
value
;
const
userAgent
=
headersList
.
get
(
"user-agent"
)
;
return
<
div
>
...
</
div
>
;
}
Params and searchParams are also async:
export
default
async
function
Page
(
{
params
,
searchParams
,
}
:
{
params
:
Promise
<
{
slug
:
string
}
>
;
searchParams
:
Promise
<
{
sort
?
:
string
}
>
;
}
)
{
const
{
slug
}
=
await
params
;
const
{
sort
}
=
await
searchParams
;
// ...
}
See
references/nextjs16-migration.md
for migration guide and proxy.ts configuration.
Implement Parallel Routes
Use
@folder
convention for parallel route slots.
// app/dashboard/layout.tsx
export
default
function
DashboardLayout
(
{
children
,
team
,
analytics
,
}
:
{
children
:
React
.
ReactNode
;
team
:
React
.
ReactNode
;
analytics
:
React
.
ReactNode
;
}
)
{
return
(
<
div
>
{
children
}
<
div
className
=
"
grid grid-cols-2
"
>
{
team
}
{
analytics
}
</
div
>
</
div
>
)
;
}
// app/dashboard/@team/page.tsx
export
default
function
TeamPage
(
)
{
return
<
div
>
Team Section
</
div
>
;
}
// app/dashboard/@analytics/page.tsx
export
default
function
AnalyticsPage
(
)
{
return
<
div
>
Analytics Section
</
div
>
;
}
See
references/routing-patterns.md
for intercepting routes, route groups, and dynamic routes.
Best Practices
Server vs Client Decision
Start with Server Component (default)
Use Client Component only for:
React hooks (useState, useEffect, useContext)
Browser APIs (window, document, localStorage)
Event handlers (onClick, onSubmit)
Client-only libraries
Data Fetching
Fetch in Server Components when possible
Use React's
cache()
for deduplication
Parallelize independent fetches
Add Suspense boundaries with
loading.tsx
Caching Strategy
"use cache"
;
import
{
cacheLife
,
cacheTag
}
from
"next/cache"
;
// Set cache duration
cacheLife
(
"hours"
)
;
// Tag for revalidation
cacheTag
(
"resource-name"
)
;
Performance Checklist
Use
loading.tsx
for Suspense boundaries
Use
next/image
for optimized images
Use
next/font
for font optimization
Enable React Compiler in
next.config.ts
Add
error.tsx
for error handling
Add
not-found.tsx
for 404 handling
Examples
Example 1: Create Blog Post with Server Action
Input:
Create a form to submit blog posts with validation
Output:
// app/blog/actions.ts
"use server"
;
import
{
z
}
from
"zod"
;
import
{
revalidatePath
}
from
"next/cache"
;
const
schema
=
z
.
object
(
{
title
:
z
.
string
(
)
.
min
(
5
)
,
content
:
z
.
string
(
)
.
min
(
10
)
,
}
)
;
export
async
function
createPost
(
formData
:
FormData
)
{
const
parsed
=
schema
.
safeParse
(
{
title
:
formData
.
get
(
"title"
)
,
content
:
formData
.
get
(
"content"
)
,
}
)
;
if
(
!
parsed
.
success
)
{
return
{
errors
:
parsed
.
error
.
flatten
(
)
.
fieldErrors
}
;
}
await
db
.
post
.
create
(
{
data
:
parsed
.
data
}
)
;
revalidatePath
(
"/blog"
)
;
return
{
success
:
true
}
;
}
// app/blog/new/page.tsx
"use client"
;
import
{
useActionState
}
from
"react"
;
import
{
createPost
}
from
"../actions"
;
export
default
function
NewPostPage
(
)
{
const
[
state
,
formAction
,
pending
]
=
useActionState
(
createPost
,
{
}
)
;
return
(
<
form
action
=
{
formAction
}
>
<
input
name
=
"
title
"
placeholder
=
"
Title
"
/>
{
state
.
errors
?.
title
&&
<
span
>
{
state
.
errors
.
title
[
0
]
}
</
span
>
}
<
textarea
name
=
"
content
"
placeholder
=
"
Content
"
/>
{
state
.
errors
?.
content
&&
<
span
>
{
state
.
errors
.
content
[
0
]
}
</
span
>
}
<
button
type
=
"
submit
"
disabled
=
{
pending
}
>
{
pending
?
"Publishing..."
:
"Publish"
}
</
button
>
</
form
>
)
;
}
Example 2: Product Page with Caching
Input:
Create a cached product page with revalidation
Output:
// app/products/[id]/page.tsx
"use cache"
;
import
{
cacheLife
,
cacheTag
}
from
"next/cache"
;
export
default
async
function
ProductPage
(
{
params
,
}
:
{
params
:
Promise
<
{
id
:
string
}
>
;
}
)
{
const
{
id
}
=
await
params
;
cacheTag
(
`
product-
${
id
}
`
,
"products"
)
;
cacheLife
(
"hours"
)
;
const
product
=
await
db
.
product
.
findUnique
(
{
where
:
{
id
}
}
)
;
if
(
!
product
)
{
notFound
(
)
;
}
return
(
<
article
>
<
h1
>
{
product
.
name
}
</
h1
>
<
p
>
{
product
.
description
}
</
p
>
<
p
>
$
{
product
.
price
}
</
p
>
</
article
>
)
;
}
// app/api/revalidate/route.ts
import
{
revalidateTag
}
from
"next/cache"
;
import
{
NextResponse
}
from
"next/server"
;
export
async
function
POST
(
request
:
Request
)
{
const
{
tag
}
=
await
request
.
json
(
)
;
revalidateTag
(
tag
)
;
return
NextResponse
.
json
(
{
revalidated
:
true
}
)
;
}
Example 3: Dashboard with Parallel Routes
Input:
Create a dashboard with sidebar and main content areas
Output:
// app/dashboard/layout.tsx
export
default
function
DashboardLayout
(
{
children
,
sidebar
,
stats
,
}
:
{
children
:
React
.
ReactNode
;
sidebar
:
React
.
ReactNode
;
stats
:
React
.
ReactNode
;
}
)
{
return
(
<
div
className
=
"
flex
"
>
<
aside
className
=
"
w-64
"
>
{
sidebar
}
</
aside
>
<
main
className
=
"
flex-1
"
>
<
div
className
=
"
grid grid-cols-3
"
>
{
stats
}
</
div
>
{
children
}
</
main
>
</
div
>
)
;
}
// app/dashboard/@sidebar/page.tsx
export
default
function
Sidebar
(
)
{
return
<
nav
>
{
/ Navigation links /
}
</
nav
>
;
}
// app/dashboard/@stats/page.tsx
export
default
async
function
Stats
(
)
{
const
stats
=
await
fetchStats
(
)
;
return
(
<
>
<
div
>
Users:
{
stats
.
users
}
</
div
>
<
div
>
Orders:
{
stats
.
orders
}
</
div
>
<
div
>
Revenue:
{
stats
.
revenue
}
</
div
>
</
>
)
;
}
Constraints and Warnings
Constraints
Server Components cannot use browser APIs or React hooks
Client Components cannot be async (no direct data fetching)
cookies()
,
headers()
,
draftMode()
are async in Next.js 16
params
and
searchParams
are Promise-based in Next.js 16
Server Actions must be defined with "use server" directive
Warnings
Attempting to use
await
in a Client Component will cause a build error
Accessing
window
or
document
in Server Components will throw an error
Forgetting to await
cookies()
or
headers()
in Next.js 16 will result in a Promise instead of the actual values
Server Actions without proper validation can expose your database to unauthorized access
External Data Fetching
Server Components that fetch data from external APIs ( fetch() calls to third-party URLs) process untrusted content; always validate, sanitize, and type-check fetched responses before rendering, and use environment variables for API URLs rather than hardcoding them References Consult these files for detailed patterns: references/app-router-fundamentals.md - Server/Client Components, file conventions, navigation, next/image, next/font references/routing-patterns.md - Parallel routes, intercepting routes, route groups, dynamic routes references/caching-strategies.md - "use cache", cacheLife, cacheTag, revalidation references/server-actions.md - Server Actions, useActionState, validation, optimistic updates references/nextjs16-migration.md - Async APIs, proxy.ts, Turbopack, config references/data-fetching.md - Data patterns, Suspense, streaming references/metadata-api.md - generateMetadata, OpenGraph, sitemap
返回排行榜