Next.js App Router Server Components First
Prioritize React Server Components for data fetching and static content. Client components only when interactivity is required.
Overview
This rule establishes the foundational architecture pattern for Next.js 14+ applications using the App Router. Server Components represent a paradigm shift in how React applications handle rendering and data fetching. By default, all components in the App Router are Server Components, which means they execute exclusively on the server, sending only HTML to the client. This approach dramatically reduces the JavaScript bundle size sent to the browser, improving Time to Interactive (TTI) metrics significantly. The architectural principle here is separation of concerns at the component level. Server Components should handle all data fetching, access backend resources directly, and render static content. When a component requires state, event handlers, or browser APIs, it must be marked with the 'use client' directive. However, this boundary should be drawn carefully to avoid overusing Client Components, which defeat the performance benefits of the App Router architecture. Best practices include colocating data fetching with components that need it using async/await, utilizing caching strategies with fetch() and revalidate options, and leveraging streaming with Suspense boundaries for improved perceived performance. The key insight is that Server Components are not just a rendering optimization—they enable direct database and filesystem access from components, eliminating the need for API routes in many scenarios. When implementing this pattern, consider the composition zone where Server and Client Components interact. Server Components can import and render Client Components, but Client Components cannot import Server Components. However, Client Components can accept Server Component children as props, enabling creative composition patterns that maintain the performance benefits while supporting rich interactivity.
Code Example
'use server';
import { db } from '@/lib/database';
import { cache } from 'react';
export const getProjects = cache(async () => {
return db.project.findMany({
orderBy: { createdAt: 'desc' },
take: 10,
});
});
export async function ProjectList() {
const projects = await getProjects();
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
);
}More Next.js Rules
Next.js Image Optimization Best Practices
Use the Image component with proper sizing, formats, and loading strategies for maximum performance.
import Image from 'next/image';
import Link from 'next/link';
export function ProjectCard({ project }: { project: Project }) {
return (
<Link h...Next.js Middleware Authentication
Implement authentication at the edge with Next.js Middleware for protected routes.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { jwtVerify } from 'jose';
const JWT_SECRET = new ...Next.js Dynamic Metadata API
Use the Metadata API for SEO optimization with dynamic titles, descriptions, and OpenGraph images.
import { Metadata, ResolvingMetadata } from 'next';
import { notFound } from 'next/navigation';
import { db } from '@/lib/db';
import { SchemaMarkup }...