92 next app router rsc review
Skill FluxonLab/Skillry/plugins/frontend-web-design/skills/92-next-app-router-rsc-review
Use when you need to review a Next.js App Router codebase for React Server Component correctness, "use client"/"use server" boundary violations, server secret leakage into the client bundle, Server Action input validation, fetch cache strategy, and metadata/streaming patterns.From its SKILL.md
npx -y skills add FluxonLab/Skillry --skill 92-next-app-router-rsc-reviewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.6 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it
Next.js App Router & RSC Review
Purpose
Audit a Next.js 15 App Router project for correctness at the Server/Client Component boundary: detect "use client" / "use server" misuse, unserializable prop passing, accidental server-secret exposure in client bundles, missing Server Action validation, incorrect fetch caching, and broken Suspense/error-boundary wiring. Surface every finding with the exact file and line — never infer from project structure alone.
When to use
- A PR adds or modifies pages, layouts, or components in the
app/directory. - A bug report mentions hydration errors, "cannot serialize a class instance", or unexpected client re-renders.
- A security review flags a
NEXT_PUBLIC_variable being used for a secret, or a server import in a client file. next buildproduces a large client bundle or a missing-server-component warning.- You are onboarding into a Next.js 15 project using the App Router for the first time.
When not to use
- The project uses only the Pages Router (
pages/directory) — use a legacy Next.js review instead. - The PR is CSS/Tailwind-only with no component logic changes.
- The issue is purely a database or API concern with no rendering layer involvement.
Procedure
1. Map the Server/Client component boundary
# Count "use client" directives
grep -rn '"use client"' app/ --include="*.tsx" --include="*.ts" | wc -l
grep -rn '"use client"' app/ --include="*.tsx" --include="*.ts" | head -30
# Count "use server" directives (Server Actions and server-only files)
grep -rn '"use server"' app/ --include="*.tsx" --include="*.ts" | wc -l
# Find components that have BOTH directives (always wrong)
grep -rln '"use client"' app/ | xargs grep -l '"use server"' 2>/dev/null
# Find "use client" at file level vs. inside a function (wrong position)
grep -n '"use client"' app/**/*.tsx | grep -v "^.*:1:" | head -20
2. Detect unserializable props passed from Server to Client Components
# Server Component passing a function prop to a client component — not serializable
grep -rn --include="*.tsx" \
'onClick={[^}]*}>\|onSubmit={[^}]*}>' \
app/ | grep -v '"use client"' | head -20
# Class instance, Map, Set, Date props — detect suspicious patterns
grep -rn --include="*.tsx" \
'new Date()\|new Map()\|new Set()' \
app/ | head -20
3. Audit for server secret leakage into client bundle
# "use client" files that import from server-only modules
grep -rln '"use client"' app/ | xargs grep -l \
"server-only\|@/lib/db\|prisma\|supabase.*service_role\|process\.env\.[^N]" 2>/dev/null
# NEXT_PUBLIC_ misuse: secret stored as NEXT_PUBLIC_ (exposed to browser)
grep -rn "NEXT_PUBLIC_" .env* 2>/dev/null | grep -iE "secret|key|token|password|private" | head -20
# Server-only env var referenced inside a "use client" file
grep -rn 'process\.env\.' app/ --include="*.tsx" \
| grep -v "NEXT_PUBLIC_" > /tmp/server-env-refs.txt
# Cross-reference with "use client" files
grep -rln '"use client"' app/ | xargs grep -ln 'process\.env\.' 2>/dev/null
# Ensure server-only is imported at the top of server utility files
grep -rn "server-only" lib/ utils/ --include="*.ts" | head -20
4. Validate Server Actions
# Find all Server Actions (files or functions with "use server")
grep -rn '"use server"' app/ --include="*.ts" --include="*.tsx" -l
# Check for input validation (zod schema) in each action file
for f in $(grep -rln '"use server"' app/); do
if ! grep -q "z\.\|zod\|schema\.parse\|safeParse" "$f"; then
echo "NO VALIDATION: $f"
fi
done
# Check for auth guard in server actions (session/user check before mutation)
grep -rn '"use server"' app/ -l | xargs grep -L \
"getServerSession\|auth()\|currentUser\|session\." 2>/dev/null | head -10
# Confirm revalidatePath/revalidateTag is called after mutations
grep -rn "revalidatePath\|revalidateTag" app/ --include="*.ts" --include="*.tsx"
5. Review fetch caching strategy
# Find fetch calls without explicit cache option (Next.js 15 default: no-store)
grep -rn "fetch(" app/ --include="*.tsx" --include="*.ts" \
| grep -v "cache:\|next:" | head -30
# Find stale force-cache on dynamic data
grep -rn "force-cache" app/ --include="*.tsx" --include="*.ts" | head -20
# Find unstable_cache / React cache usage
grep -rn "unstable_cache\|import.*cache.*from.*react" app/ --include="*.ts" --include="*.tsx"
# Find generateStaticParams for dynamic routes
find app -name "page.tsx" | xargs grep -l "\[" 2>/dev/null \
| xargs grep -L "generateStaticParams" 2>/dev/null | head -10
6. Check metadata API and streaming/Suspense
# Pages/layouts missing metadata export
find app -name "page.tsx" | xargs grep -L "export const metadata\|generateMetadata" | head -20
# Suspense boundaries wrapping async components
grep -rn "<Suspense" app/ --include="*.tsx" | wc -l
# error.tsx and loading.tsx presence per route segment
find app -type d | while read d; do
has_page=$(ls "$d/page.tsx" 2>/dev/null)
has_error=$(ls "$d/error.tsx" 2>/dev/null)
has_loading=$(ls "$d/loading.tsx" 2>/dev/null)
[ -n "$has_page" ] && [ -z "$has_error" ] && echo "NO error.tsx: $d"
done
7. Inspect data-fetching patterns in RSC
# Confirm await fetch / await db calls are at RSC level (not inside useEffect)
grep -rn "useEffect.*fetch\|useEffect.*await" app/ --include="*.tsx" | head -20
# Client-side data fetch that should be RSC fetch
grep -rn "useEffect\|useState" app/ --include="*.tsx" \
| grep -v '"use client"' > /tmp/hooks-without-use-client.txt
wc -l /tmp/hooks-without-use-client.txt
# Prisma or DB client imported outside server context
grep -rn "prisma\|PrismaClient" app/ --include="*.tsx" --include="*.ts" \
| grep -v "\.server\." | head -20
Concrete checks
- Every file containing React hooks (
useState,useEffect,useRef, etc.) has"use client"at line 1. - No
"use client"file importsserver-only,prisma, or any module that transitively imports Node-only APIs. - No
NEXT_PUBLIC_variable holds a secret, API key, or private token. - Server-only env vars (
process.env.DATABASE_URL,process.env.SECRET_*) are never referenced in"use client"files. - Every Server Action has a zod (or equivalent)
parse/safeParseon incomingFormDataor arguments. - Every mutating Server Action checks the session/user before performing the mutation.
-
revalidatePathorrevalidateTagis called after each Server Action that mutates cached data. -
fetch()calls in RSC have an explicitcache:ornext: { revalidate }option — no accidental over-caching. -
generateStaticParamsis exported from every dynamic-segment page that should be statically generated. - Every async RSC that may suspend is wrapped in a
<Suspense fallback={...}>boundary. -
error.tsxfiles are present for every route segment with meaningful data-fetching. -
loading.tsxis present for route segments with slow data dependencies. -
export const dynamic = 'force-dynamic'is used intentionally, not as a catch-all fix for caching issues. - The
client-onlypackage is imported in any file that must never run on the server. - No class instances, functions, or non-serializable objects are passed as props across the Server/Client boundary.
Commands
# Summary: boundary violation candidates
echo "=== 'use client' files with server imports ==="
grep -rln '"use client"' app/ | xargs grep -l \
"prisma\|server-only\|process\.env\.[^N]" 2>/dev/null
echo "=== Server Actions without zod validation ==="
for f in $(grep -rln '"use server"' app/); do
grep -q "safeParse\|schema\.parse\|z\." "$f" || echo " $f"
done
echo "=== Pages missing metadata ==="
find app -name "page.tsx" | xargs grep -rL "metadata\|generateMetadata" | head -20
echo "=== fetch() without cache option ==="
grep -rn 'fetch(' app/ --include="*.ts" --include="*.tsx" \
| grep -v "cache:\|next:\|//\|test" | wc -l
Required output
Produce a structured report with:
- Boundary audit — list every
"use client"file that imports server-only modules; severity Critical if it leaks secrets. - Secret/env audit — any
NEXT_PUBLIC_secret or server env ref in client files; severity Critical. - Server Action findings — files missing input validation or auth guard, with exact path.
- Cache strategy — fetch calls with missing or wrong cache mode,
generateStaticParamsgaps. - Streaming/metadata gaps — pages missing
error.tsx,loading.tsx,<Suspense>, ormetadataexport. - Recommended fixes — one concrete code snippet per Critical/High finding.
- Next safe command — the single highest-priority action.
Safety checks
- Do not modify
.env*files during review. - Do not execute Server Actions or trigger mutations as part of the review.
- Do not force-push or amend history to remove accidentally committed secrets; flag them for rotation and
.gitignoreupdate instead. - Never print raw env var values found in files.
Completion criteria
Done means: all "use client" / "use server" files are checked for boundary correctness, secret leakage paths are enumerated, every Server Action has a validation/auth status, fetch caching strategy is assessed for each RSC data-fetch, and Suspense/error boundary coverage is documented. Every finding has file:line + severity + a concrete fix.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.