12 KiB
12 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | Routing | Use App Router for new projects | App Router is the recommended approach in Next.js 14+ | app/ directory with page.tsx | pages/ for new projects | app/dashboard/page.tsx | pages/dashboard.tsx | Medium | https://nextjs.org/docs/app |
| 3 | 2 | Routing | Use file-based routing | Create routes by adding files in app directory | page.tsx for routes layout.tsx for layouts | Manual route configuration | app/blog/[slug]/page.tsx | Custom router setup | Medium | https://nextjs.org/docs/app/building-your-application/routing |
| 4 | 3 | Routing | Colocate related files | Keep components styles tests with their routes | Component files alongside page.tsx | Separate components folder | app/dashboard/_components/ | components/dashboard/ | Low | |
| 5 | 4 | Routing | Use route groups for organization | Group routes without affecting URL | Parentheses for route groups | Nested folders affecting URL | (marketing)/about/page.tsx | marketing/about/page.tsx | Low | https://nextjs.org/docs/app/building-your-application/routing/route-groups |
| 6 | 5 | Routing | Handle loading states | Use loading.tsx for route loading UI | loading.tsx alongside page.tsx | Manual loading state management | app/dashboard/loading.tsx | useState for loading in page | Medium | https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming |
| 7 | 6 | Routing | Handle errors with error.tsx | Catch errors at route level | error.tsx with reset function | try/catch in every component | app/dashboard/error.tsx | try/catch in page component | High | https://nextjs.org/docs/app/building-your-application/routing/error-handling |
| 8 | 7 | Rendering | Use Server Components by default | Server Components reduce client JS bundle | Keep components server by default | Add 'use client' unnecessarily | export default function Page() | ('use client') for static content | High | https://nextjs.org/docs/app/building-your-application/rendering/server-components |
| 9 | 8 | Rendering | Mark Client Components explicitly | 'use client' for interactive components | Add 'use client' only when needed | Server Component with hooks/events | ('use client') for onClick useState | No directive with useState | High | https://nextjs.org/docs/app/building-your-application/rendering/client-components |
| 10 | 9 | Rendering | Push Client Components down | Keep Client Components as leaf nodes | Client wrapper for interactive parts only | Mark page as Client Component | <InteractiveButton/> in Server Page | ('use client') on page.tsx | High | |
| 11 | 10 | Rendering | Use streaming for better UX | Stream content with Suspense boundaries | Suspense for slow data fetches | Wait for all data before render | <Suspense><SlowComponent/></Suspense> | await allData then render | Medium | https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming |
| 12 | 11 | Rendering | Choose correct rendering strategy | SSG for static SSR for dynamic ISR for semi-static | generateStaticParams for known paths | SSR for static content | export const revalidate = 3600 | fetch without cache config | Medium | |
| 13 | 12 | DataFetching | Fetch data in Server Components | Fetch directly in async Server Components | async function Page() { const data = await fetch() } | useEffect for initial data | const data = await fetch(url) | useEffect(() => fetch(url)) | High | https://nextjs.org/docs/app/building-your-application/data-fetching |
| 14 | 13 | DataFetching | Configure caching explicitly (Next.js 15+) | Next.js 15 changed defaults to uncached for fetch | Explicitly set cache: 'force-cache' for static data | Assume default is cached (it's not in Next.js 15) | fetch(url { cache: 'force-cache' }) | fetch(url) // Uncached in v15 | High | https://nextjs.org/docs/app/building-your-application/upgrading/version-15 |
| 15 | 14 | DataFetching | Deduplicate fetch requests | React and Next.js dedupe same requests | Same fetch call in multiple components | Manual request deduplication | Multiple components fetch same URL | Custom cache layer | Low | |
| 16 | 15 | DataFetching | Use Server Actions for mutations | Server Actions for form submissions | action={serverAction} in forms | API route for every mutation | <form action={createPost}> | <form onSubmit={callApiRoute}> | Medium | https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations |
| 17 | 16 | DataFetching | Revalidate data appropriately | Use revalidatePath/revalidateTag after mutations | Revalidate after Server Action | 'use client' with manual refetch | revalidatePath('/posts') | router.refresh() everywhere | Medium | https://nextjs.org/docs/app/building-your-application/caching#revalidating |
| 18 | 17 | Images | Use next/image for optimization | Automatic image optimization and lazy loading | <Image> component for all images | <img> tags directly | <Image src={} alt={} width={} height={}> | <img src={}/> | High | https://nextjs.org/docs/app/building-your-application/optimizing/images |
| 19 | 18 | Images | Provide width and height | Prevent layout shift with dimensions | width and height props or fill | Missing dimensions | <Image width={400} height={300}/> | <Image src={url}/> | High | |
| 20 | 19 | Images | Use fill for responsive images | Fill container with object-fit | fill prop with relative parent | Fixed dimensions for responsive | <Image fill className="object-cover"/> | <Image width={window.width}/> | Medium | |
| 21 | 20 | Images | Configure remote image domains | Whitelist external image sources | remotePatterns in next.config.js | Allow all domains | remotePatterns: [{ hostname: 'cdn.example.com' }] | domains: ['*'] | High | https://nextjs.org/docs/app/api-reference/components/image#remotepatterns |
| 22 | 21 | Images | Use priority for LCP images | Mark above-fold images as priority | priority prop on hero images | All images with priority | <Image priority src={hero}/> | <Image priority/> on every image | Medium | |
| 23 | 22 | Fonts | Use next/font for fonts | Self-hosted fonts with zero layout shift | next/font/google or next/font/local | External font links | import { Inter } from 'next/font/google' | <link href="fonts.googleapis.com"/> | Medium | https://nextjs.org/docs/app/building-your-application/optimizing/fonts |
| 24 | 23 | Fonts | Apply font to layout | Set font in root layout for consistency | className on body in layout.tsx | Font in individual pages | <body className={inter.className}> | Each page imports font | Low | |
| 25 | 24 | Fonts | Use variable fonts | Variable fonts reduce bundle size | Single variable font file | Multiple font weights as files | Inter({ subsets: ['latin'] }) | Inter_400 Inter_500 Inter_700 | Low | |
| 26 | 25 | Metadata | Use generateMetadata for dynamic | Generate metadata based on params | export async function generateMetadata() | Hardcoded metadata everywhere | generateMetadata({ params }) | export const metadata = {} | Medium | https://nextjs.org/docs/app/building-your-application/optimizing/metadata |
| 27 | 26 | Metadata | Include OpenGraph images | Add OG images for social sharing | opengraph-image.tsx or og property | Missing social preview images | opengraph: { images: ['/og.png'] } | No OG configuration | Medium | |
| 28 | 27 | Metadata | Use metadata API | Export metadata object for static metadata | export const metadata = {} | Manual head tags | export const metadata = { title: 'Page' } | <head><title>Page</title></head> | Medium | |
| 29 | 28 | API | Use Route Handlers for APIs | app/api routes for API endpoints | app/api/users/route.ts | pages/api for new projects | export async function GET(request) | export default function handler | Medium | https://nextjs.org/docs/app/building-your-application/routing/route-handlers |
| 30 | 29 | API | Return proper Response objects | Use NextResponse for API responses | NextResponse.json() for JSON | Plain objects or res.json() | return NextResponse.json({ data }) | return { data } | Medium | |
| 31 | 30 | API | Handle HTTP methods explicitly | Export named functions for methods | Export GET POST PUT DELETE | Single handler for all methods | export async function POST() | switch(req.method) | Low | |
| 32 | 31 | API | Validate request body | Validate input before processing | Zod or similar for validation | Trust client input | const body = schema.parse(await req.json()) | const body = await req.json() | High | |
| 33 | 32 | Middleware | Use middleware for auth | Protect routes with middleware.ts | middleware.ts at root | Auth check in every page | export function middleware(request) | if (!session) redirect in page | Medium | https://nextjs.org/docs/app/building-your-application/routing/middleware |
| 34 | 33 | Middleware | Match specific paths | Configure middleware matcher | config.matcher for specific routes | Run middleware on all routes | matcher: ['/dashboard/:path*'] | No matcher config | Medium | |
| 35 | 34 | Middleware | Keep middleware edge-compatible | Middleware runs on Edge runtime | Edge-compatible code only | Node.js APIs in middleware | Edge-compatible auth check | fs.readFile in middleware | High | |
| 36 | 35 | Environment | Use NEXT_PUBLIC prefix | Client-accessible env vars need prefix | NEXT_PUBLIC_ for client vars | Server vars exposed to client | NEXT_PUBLIC_API_URL | API_SECRET in client code | High | https://nextjs.org/docs/app/building-your-application/configuring/environment-variables |
| 37 | 36 | Environment | Validate env vars | Check required env vars exist | Validate on startup | Undefined env at runtime | if (!process.env.DATABASE_URL) throw | process.env.DATABASE_URL (might be undefined) | High | |
| 38 | 37 | Environment | Use .env.local for secrets | Local env file for development secrets | .env.local gitignored | Secrets in .env committed | .env.local with secrets | .env with DATABASE_PASSWORD | High | |
| 39 | 38 | Performance | Analyze bundle size | Use @next/bundle-analyzer | Bundle analyzer in dev | Ship large bundles blindly | ANALYZE=true npm run build | No bundle analysis | Medium | https://nextjs.org/docs/app/building-your-application/optimizing/bundle-analyzer |
| 40 | 39 | Performance | Use dynamic imports | Code split with next/dynamic | dynamic() for heavy components | Import everything statically | const Chart = dynamic(() => import('./Chart')) | import Chart from './Chart' | Medium | https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading |
| 41 | 40 | Performance | Avoid layout shifts | Reserve space for dynamic content | Skeleton loaders aspect ratios | Content popping in | <Skeleton className="h-48"/> | No placeholder for async content | High | |
| 42 | 41 | Performance | Use Partial Prerendering | Combine static and dynamic in one route | Static shell with Suspense holes | Full dynamic or static pages | Static header + dynamic content | Entire page SSR | Low | https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering |
| 43 | 42 | Link | Use next/link for navigation | Client-side navigation with prefetching | <Link href=""> for internal links | <a> for internal navigation | <Link href="/about">About</Link> | <a href="/about">About</a> | High | https://nextjs.org/docs/app/api-reference/components/link |
| 44 | 43 | Link | Prefetch strategically | Control prefetching behavior | prefetch={false} for low-priority | Prefetch all links | <Link prefetch={false}> | Default prefetch on every link | Low | |
| 45 | 44 | Link | Use scroll option appropriately | Control scroll behavior on navigation | scroll={false} for tabs pagination | Always scroll to top | <Link scroll={false}> | Manual scroll management | Low | |
| 46 | 45 | Config | Use next.config.js correctly | Configure Next.js behavior | Proper config options | Deprecated or wrong options | images: { remotePatterns: [] } | images: { domains: [] } | Medium | https://nextjs.org/docs/app/api-reference/next-config-js |
| 47 | 46 | Config | Enable strict mode | Catch potential issues early | reactStrictMode: true | Strict mode disabled | reactStrictMode: true | reactStrictMode: false | Medium | |
| 48 | 47 | Config | Configure redirects and rewrites | Use config for URL management | redirects() rewrites() in config | Manual redirect handling | redirects: async () => [...] | res.redirect in pages | Medium | https://nextjs.org/docs/app/api-reference/next-config-js/redirects |
| 49 | 48 | Deployment | Use Vercel for easiest deploy | Vercel optimized for Next.js | Deploy to Vercel | Self-host without knowledge | vercel deploy | Complex Docker setup for simple app | Low | https://nextjs.org/docs/app/building-your-application/deploying |
| 50 | 49 | Deployment | Configure output for self-hosting | Set output option for deployment target | output: 'standalone' for Docker | Default output for containers | output: 'standalone' | No output config for Docker | Medium | https://nextjs.org/docs/app/building-your-application/deploying#self-hosting |
| 51 | 50 | Security | Sanitize user input | Never trust user input | Escape sanitize validate all input | Direct interpolation of user data | DOMPurify.sanitize(userInput) | dangerouslySetInnerHTML={{ __html: userInput }} | High | |
| 52 | 51 | Security | Use CSP headers | Content Security Policy for XSS protection | Configure CSP in next.config.js | No security headers | headers() with CSP | No CSP configuration | High | https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy |
| 53 | 52 | Security | Validate Server Action input | Server Actions are public endpoints | Validate and authorize in Server Action | Trust Server Action input | Auth check + validation in action | Direct database call without check | High |