Skip to main content

Routing pattern consistency

ab-000231 · ai-slop-code-drift.convention-drift.routing-pattern-consistency
Severity: mediumactive

Why it matters

Next.js routing resolution between app/ and pages/ follows non-obvious precedence rules: App Router wins for matching segments, but Pages Router catches everything App Router does not claim. Deploying a half-migrated app can change which route renders simply because someone added or removed a file, with no local indication. SEO metadata, layouts, and data-fetching conventions differ completely between the two routers, producing broken canonical URLs and duplicated or missing OG tags.

Severity rationale

Medium because resolution ambiguity can swap which page renders at a given URL without any code change to that page.

Remediation

Finish the migration to App Router. Enumerate what remains:

find pages -type f -name "*.tsx" | grep -vE '_app|_document|_error|api/'

Move each remaining page into app/ (e.g., pages/about.tsx becomes app/about/page.tsx), port getServerSideProps to server components or route handlers, then delete the old file. Keep pages/api/ until those endpoints are migrated to app/api/ route handlers. Redeploy and verify every URL resolves through the expected router.

Detection

  • ID: ai-slop-code-drift.convention-drift.routing-pattern-consistency

  • Severity: medium

  • What to look for: When the framework is Next.js, check whether BOTH app/ AND pages/ directories exist at the project root or under src/. Enumerate every file under each. Pages Router files (pages/index.tsx, pages/about.tsx, etc.) and App Router files (app/page.tsx, app/about/page.tsx, etc.) are mutually exclusive routing systems. EXCEPT: a pages/ directory containing ONLY _app.{tsx,jsx,ts,js}, _document.{tsx,jsx,ts,js}, _error.{tsx,jsx,ts,js}, OR pages/api/** files (legacy API routes are allowed alongside App Router) is acceptable. Count all non-allowed files in pages/ when app/ also exists. The threshold for failure is at least 1 non-allowlisted file in pages/ when app/ also has page files.

  • Pass criteria: When framework is Next.js: only one routing system in active use, OR app/ is the active router AND pages/ only contains the allowlisted exception files. Report even on pass: "Routing pattern: App Router (or Pages Router). [N] route files."

  • Fail criteria: Both app/ and pages/ contain non-allowlisted route files — mid-migration state with unclear ownership.

  • Skip (N/A) when: Framework is not Next.js OR only one of app//pages/ exists.

  • Detail on fail: "Both routing patterns active: app/ has 12 page files, pages/ has 5 page files (excluding _app, _document, api/). The two routers conflict — pick one and finish the migration."

  • Remediation: Mixing App Router and Pages Router pages means each route's behavior depends on which router resolves it first — and Next.js's resolution rules are not obvious. Pick the destination and finish the migration:

    # Inventory non-allowlisted pages/ files
    find pages -type f -name "*.tsx" | grep -vE '_app|_document|_error|api/'
    
    # Move each to app/ following the new conventions
    # pages/about.tsx → app/about/page.tsx
    # Then delete the pages/ file
    

Taxons

History