| name | next-best-practices |
| description | Next.js 15 App Router best practices for this landing page project. Use when working with layouts, pages, metadata, RSC boundaries, fonts, images, error handling, or SEO optimization. |
Next.js Best Practices (App Router)
File Conventions
layout.tsx — persistent shell (providers, fonts, metadata)
page.tsx — route entry point
error.tsx — error boundary per route
not-found.tsx — 404 fallback
loading.tsx — Suspense fallback
RSC Boundaries
Server Components are default. Only add "use client" when the component needs:
- Event handlers (onClick, onChange)
- Browser APIs (window, document, localStorage)
- React hooks that use state/effects (useState, useEffect)
- Framer Motion animations (
motion.* components)
Push "use client" as far down the tree as possible. Section composition files (*-section.tsx) stay as Server Components.
Invalid patterns
- Async client components (
"use client" + async function)
- Passing non-serializable props (functions, classes) from Server to Client
- Importing server-only code in client components
Metadata API
Always export metadata from layout.tsx or page.tsx:
export const metadata: Metadata = {
title: "FlowPilot — AI-Powered Workspace",
description: "Organize ideas, tasks, and decisions in one place.",
openGraph: {
title: "FlowPilot",
description: "AI-powered workspace for teams",
type: "website",
},
};
Use generateMetadata for dynamic routes only.
Font Optimization
Always use next/font — never load fonts via <link> or @import in CSS:
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
This self-hosts fonts (zero external requests) and eliminates layout shift.
Image Optimization
Always use next/image over <img>:
- Set explicit
width and height or use fill with a sized parent
- Add
priority to LCP images (hero section)
- Use
sizes attribute for responsive images
- Use blur placeholders for below-fold images
Data Patterns
For this landing page:
- Static data lives in
*.data.ts files (plain TS objects)
- No
fetch(), no API routes, no server actions needed
- Section data is typed and imported at the Section layer
Error Handling
- Use
error.tsx for route-level error boundaries
- Use
not-found.tsx for 404 pages
error.tsx must be a Client Component ("use client")
- Call
notFound() from server code, never from client
Performance
- Server Components render on the server — zero client JS by default
- Avoid importing large client libraries at the layout level
- Use dynamic imports for heavy client components below the fold
- Use
<Suspense> boundaries around async components
- Target: LCP < 2.5s, CLS < 0.1, Lighthouse Performance > 90
Hydration
Common causes of hydration mismatch:
- Using
Date.now() or Math.random() in render
- Checking
window or document during SSR
- Browser extensions modifying the DOM
- Invalid HTML nesting (
<p> inside <p>, <div> inside <p>)
Fix: wrap browser-only code in useEffect or use suppressHydrationWarning.
Deployment (Vercel)
- Zero-config for Next.js
- Preview URLs per branch
next.config.ts (not .js) for TypeScript config
- Use
output: "standalone" only for Docker deployments
File Naming
- All files use
kebab-case
- Pages and layouts use Next.js conventions (lowercase)
- Components use
kebab-case.tsx
- Types use
kebab-case.ts