$ npx rulesync-cli pull✓ Wrote CLAUDE.md (2 rulesets)# Coding Standards- Always use async/await- Prefer named exports
Rule Writing

CLAUDE.md for Next.js Pages Router

Many production Next.js apps still use the Pages Router. Rules for getServerSideProps, getStaticProps, API routes, and the patterns that still matter.

7 min read·May 9, 2024

Thousands of apps still run Pages Router — they need rules too

getServerSideProps, getStaticProps, ISR, API routes, and _app/_document patterns

Why Pages Router Still Needs Dedicated Rules

Despite the App Router being the recommended approach for new projects, thousands of production Next.js applications run on the Pages Router — and will continue to for years. Migration is expensive and risky for large apps. These teams need AI rules that generate correct Pages Router patterns, not App Router patterns leaked into a Pages Router codebase.

The problem is bidirectional: AI assistants either generate App Router patterns in Pages Router projects (React Server Components, server actions, app/ directory) or generate outdated Pages Router patterns (getInitialProps, custom server). Your rules must specify exactly which router you use and which data fetching patterns are current.

These rules target Next.js 12-14 with Pages Router. If you're on Next.js 14+ with App Router, use the App Router rules article instead.

Rule 1: Data Fetching with getServerSideProps and getStaticProps

The rule: 'Use getServerSideProps for pages that need fresh data on every request (user-specific content, real-time data). Use getStaticProps for pages that can be pre-rendered at build time (blog posts, marketing pages). Use getStaticPaths with getStaticProps for dynamic routes. Never use getInitialProps — it's legacy and runs on both server and client, causing hydration issues.'

For ISR (Incremental Static Regeneration): 'Use revalidate in getStaticProps for pages that are mostly static but need periodic updates: return { props: data, revalidate: 60 }. This gives you the performance of static with the freshness of server-rendered. Use on-demand revalidation (res.revalidate) for webhook-triggered updates.'

For the client side: 'Use SWR or React Query for client-side data fetching (search results, infinite scroll, user-initiated refreshes). Fetch initial data with getServerSideProps, then use SWR for subsequent updates. Never fetch initial page data in useEffect — it causes layout shift and poor SEO.'

  • getServerSideProps: fresh data per request — user-specific, real-time
  • getStaticProps: pre-rendered at build — blog posts, marketing, docs
  • getStaticPaths + getStaticProps: dynamic routes with static generation
  • ISR with revalidate: static performance, periodic freshness
  • Never getInitialProps — legacy pattern with hydration issues
  • SWR/React Query for client-side only — never useEffect for initial data
💡 ISR = Best of Both

Incremental Static Regeneration gives you static performance with periodic freshness. return { props: data, revalidate: 60 } rebuilds the page every 60 seconds in the background. Users always get a fast cached response.

Rule 2: API Routes in pages/api/

The rule: 'API routes live in pages/api/[route].ts. Export a default function handler: export default async function handler(req: NextApiRequest, res: NextApiResponse). Use req.method to dispatch: if (req.method === "POST"). Return JSON with res.status(200).json({ data }). Never import server-only code (database, secrets) in page components — keep it in API routes or getServerSideProps.'

For middleware: 'Use Next.js middleware (middleware.ts at root) for cross-cutting concerns: authentication, redirects, geolocation, A/B testing. Middleware runs on the edge — keep it lightweight. For heavy processing, delegate to API routes. Never use custom Express middleware — use Next.js patterns.'

For validation: 'Validate all API route inputs. Use Zod or a validation library. Parse req.body before processing. Return 400 with structured error messages for invalid input. Never trust req.body types — they're any by default. Create typed request/response interfaces for every API route.'

Rule 3: _app.tsx and _document.tsx

The rule: '_app.tsx wraps every page — use it for: global CSS imports, layout components that persist across navigation, global state providers (auth, theme, query client), and page transition effects. _document.tsx customizes the HTML shell — use it for: custom fonts (<link>), language attribute on <html>, and body attributes. Never fetch data in _app or _document.'

For providers: 'Stack providers in _app.tsx in order: outermost = error boundary, then auth provider, then theme provider, then query client provider, innermost = layout. Use the Component and pageProps pattern: <Component {...pageProps} />. Pass page-level data through pageProps from getServerSideProps/getStaticProps.'

For layouts: 'Define a getLayout pattern for per-page layouts: each page exports a getLayout function that wraps the page in its specific layout. The _app.tsx calls getLayout: const getLayout = Component.getLayout ?? ((page) => page). This avoids remounting the layout on navigation.'

⚠️ No Data in _app

Never fetch data in _app.tsx or _document.tsx. _app wraps every page and runs on every navigation. Data fetching there blocks every page transition. Use getServerSideProps in individual pages instead.

Rule 4: Performance Optimization

The rule: 'Use next/image for all images — it handles lazy loading, responsive sizing, and WebP conversion. Use next/link for all internal navigation — it prefetches linked pages. Use next/dynamic for code splitting heavy components: const Editor = dynamic(() => import("./Editor"), { ssr: false }). Use next/script for third-party scripts with strategy="lazyOnload" for non-critical scripts.'

For bundle size: 'Analyze bundle size with @next/bundle-analyzer. Tree-shake imports: import { format } from "date-fns" not import * as dateFns. Avoid importing entire icon libraries: import { Check } from "lucide-react" not import * from "lucide-react". Use dynamic imports for components only needed on interaction (modals, rich text editors, charts).'

For caching: 'Set appropriate Cache-Control headers in getServerSideProps: res.setHeader("Cache-Control", "s-maxage=60, stale-while-revalidate"). Use ISR instead of SSR when the data can be slightly stale. Configure CDN caching for static assets. Never set no-cache on assets that don't change.'

  • next/image for all images — automatic lazy loading, responsive, WebP
  • next/link for navigation — automatic prefetching on hover/viewport
  • next/dynamic for code splitting — ssr: false for client-only components
  • Bundle analyzer — tree-shake imports — dynamic imports for heavy components
  • Cache-Control headers — ISR over SSR when staleness is acceptable
ℹ️ next/image Is Free Perf

next/image handles lazy loading, responsive sizing, and WebP conversion automatically. Replacing raw <img> tags with next/image typically improves LCP by 20-40% with zero code changes beyond the import.

Rule 5: SEO in Pages Router

The rule: 'Use next/head for page-level metadata: <Head><title>{pageTitle}</title><meta name="description" content={desc} /></Head>. Include Open Graph and Twitter Card meta tags. Use next-seo library for consistent SEO across pages. Generate sitemap.xml with next-sitemap. Implement canonical URLs to prevent duplicate content issues.'

For structured data: 'Add JSON-LD structured data in Head: <script type="application/ld+json">{JSON.stringify(structuredData)}</script>. Use Article schema for blog posts, Product for e-commerce, FAQ for FAQ pages. Test with Google's Rich Results Test.'

For performance SEO: 'Server-rendered pages (getServerSideProps, getStaticProps) are fully crawlable. Client-only content is not reliably indexed. Ensure all important content is rendered server-side. Use getStaticProps for content pages — they're faster and more reliably crawled than SSR.'

Complete Next.js Pages Router Rules Template

Consolidated rules for Next.js Pages Router projects.

  • getServerSideProps for dynamic, getStaticProps for static — never getInitialProps
  • ISR with revalidate for mostly-static pages — on-demand revalidation for webhooks
  • API routes in pages/api/ — validate with Zod — typed request/response
  • _app.tsx for providers and layout — _document.tsx for HTML shell only
  • getLayout pattern for per-page layouts — avoid layout remounting
  • next/image, next/link, next/dynamic, next/script — never raw <img> or <a>
  • next/head for metadata — next-seo for consistency — next-sitemap for XML sitemap
  • Bundle analyzer — tree-shake imports — Cache-Control headers on SSR pages