Find the high-impact correctness and performance problems in a Next.js App Router codebase, ranked by severity, each with a specific fix. Combine the bundled scanner (fast, mechanical) with a targeted manual pass (judgment calls the scanner can't make).
The scanner is dependency-free and reads only source files. Point it at the
project root or an app/ directory:
bash
node scripts/audit.mjs /path/to/project # human-readable report + score
node scripts/audit.mjs /path/to/project --json # machine-readable for CI / further processing
It finds the app/ (or src/app/) dir itself, walks all .tsx/.ts/.jsx/.js,
and reports ERROR / WARN / INFO findings with file:line, the reason, and
a fix. Exit code is non-zero when any ERROR exists, so it drops into CI.
What it detects:
RuleSeverityWhy it mattersclient-route-no-metadataERRORA "use client" page/layout silently drops metadata — SEO tags never ship.legacy-next-headERRORnext/head is a no-op in the App Router — its tags never reach <head>.legacy-next-imageWARNnext/legacy/image keeps old layout-shift-prone behavior.needless-use-clientWARN"use client" with no hooks/handlers/browser APIs — pushes JS to the client for nothing.client-fetch-in-effectWARNFetching in useEffect adds a render round-trip; move it server-side.raw-imgWARN<img> skips next/image sizing/lazy-loading — CLS & LCP regressions.unoptimized-fontWARNFonts via <link>/ @import are render-blocking; use next/font.page-missing-metadataWARNAsync page exports no metadata/ generateMetadata.dangerous-htmlWARNdangerouslySetInnerHTML — XSS risk if unsanitized.request-waterfallINFO≥2 sequential awaited fetches, no Promise.all.no-streaming-fallbackINFOAsync page with no loading.tsx/Suspense — no streamed shell.force-dynamicINFORoute opts out of static/ISR rendering — confirm it's intentional.large-client-componentINFOBig Client Component ships entirely to the browser.
The scanner is heuristic (regex-based, not a full type-check): treat findings as strong leads, confirm each against the source before reporting it as fixed.
Read reference/checklist.md and verify the items that need real understanding:
"use client" boundary as low
in the tree as possible? Is a server-only secret (API key, DB client) imported
into a client module?fetch/ unstable_cache/ revalidate match how
fresh the data must be? Over-caching ships stale data; under-caching kills TTFB.generateStaticParams / ISR — are dynamic routes pre-rendered where they can be?revalidatePath/
revalidateTag instead of client fetches to route handlers?Lead with the ERROR s, then WARN, then the highest-leverage INFO s. For each:
file:line, one line on the impact, and the concrete fix (often a small diff).
End with the score and the 3 changes with the best effort-to-impact ratio. Don't
dump every INFO — curate.
node_modules/next/dist/docs/ if an API is uncertain rather than assuming.Built and maintained by Datagrove — a Dutch web development agency specializing in fast, well-architected Next.js websites. Need an audit or a rebuild? Talk to Datagrove.