nextjs-app-router
Next.js App Router
Server vs Client Components
Default to Server Components. Only add 'use client' when you need:
- Event handlers (onClick, onChange, onSubmit)
- Browser APIs (localStorage, window, navigator)
- React hooks (useState, useEffect, useRef)
- Third-party client libraries
// Server Component (default) - no directive needed
export default async function Page() {
const data = await fetchData(); // Direct async/await
return <div>{data.title}</div>;
}
// Client Component - explicit directive
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
Next.js 15 Async Params (Critical)
Params and searchParams are now Promises and must be awaited:
// ✅ Correct - Next.js 15
type Props = {
params: Promise<{ locale: string; slug: string }>;
searchParams: Promise<{ [key: string]: string | undefined }>;
};
export default async function Page({ params, searchParams }: Props) {
const { locale, slug } = await params;
const { theme } = await searchParams;
return <div>Locale: {locale}, Slug: {slug}</div>;
}
// ✅ generateMetadata also uses async params
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = await params;
return { title: `Page - ${locale}` };
}
Route File Conventions
app/
├── layout.tsx # Root layout (required)
├── page.tsx # Home page (/)
├── loading.tsx # Loading UI (Suspense boundary)
├── error.tsx # Error boundary ('use client' required)
├── not-found.tsx # 404 page
├── [locale]/
│ ├── layout.tsx # Nested layout
│ ├── page.tsx # /[locale]
│ └── services/
│ ├── page.tsx # /[locale]/services
│ └── [slug]/
│ └── page.tsx # /[locale]/services/[slug]
└── api/
└── route.ts # API route handler
Layouts and Templates
// app/[locale]/layout.tsx
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
return (
<html lang={locale}>
<body>{children}</body>
</html>
);
}
Server Actions
// lib/actions.ts
'use server';
import { revalidatePath, revalidateTag } from 'next/cache';
import { redirect } from 'next/navigation';
export async function submitForm(formData: FormData) {
const email = formData.get('email') as string;
// Validate with Zod (see zod-react-hook-form skill)
// Process data...
revalidatePath('/[locale]/contact'); // Revalidate specific path
// OR revalidateTag('contact-submissions'); // Revalidate by tag
redirect('/success'); // Optional redirect
}
// Usage in Client Component
'use client';
export function ContactForm() {
return (
<form action={submitForm}>
<input name="email" type="email" required />
<button type="submit">Submit</button>
</form>
);
}
Route Handlers (API Routes)
// app/api/webhook/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const body = await request.json();
// Process webhook...
return NextResponse.json({ success: true }, { status: 200 });
}
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const id = searchParams.get('id');
return NextResponse.json({ id });
}
Data Fetching Patterns
// Server Component with fetch
async function getData() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 }, // ISR: revalidate every hour
// OR cache: 'no-store', // SSR: always fresh
// OR next: { tags: ['data'] }, // On-demand with revalidateTag
});
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}
export default async function Page() {
const data = await getData();
return <div>{data.title}</div>;
}
Static Generation
// Generate static params for dynamic routes
export async function generateStaticParams() {
const locales = ['pt-PT', 'en', 'tr', 'es', 'fr', 'de'];
const services = await getServices();
return locales.flatMap(locale =>
services.map(service => ({
locale,
slug: service.slug,
}))
);
}
Anti-Patterns to Avoid
// ❌ Don't use params directly without awaiting (Next.js 15)
export default function Page({ params }: { params: { id: string } }) {
return <div>{params.id}</div>; // Will cause errors
}
// ❌ Don't fetch in Client Components when Server Components work
'use client';
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => { fetch('/api/data')... }, []); // Unnecessary
}
// ❌ Don't use 'use client' on entire pages unless necessary
'use client';
export default function Page() {
return <div>Static content</div>; // Should be Server Component
}
// ❌ Don't import Server Components into Client Components
// Server Components can only be passed as children/props
References
For detailed patterns, see:
- PATTERNS.md - Advanced composition patterns
- DATA-FETCHING.md - Caching strategies
More from canatufkansu/claude-skills
next-intl-i18n
next-intl internationalization for 6 locales (pt-PT, en, tr, es, fr, de) with locale-prefixed routing, useTranslations/getTranslations patterns, and message file structure. Use when adding translations, creating localized pages, implementing language switchers, or handling locale routing.
45sitemap-robots
Automated sitemap generation for all locale URLs, robots.txt configuration, and llms.txt for AI crawler optimization. Use when setting up sitemap.xml, configuring crawling rules, or improving discoverability for search engines and AI systems.
35tailwind-shadcn
Tailwind CSS utility patterns with shadcn/ui component usage, theming via CSS variables, and responsive design. Use when styling components, installing shadcn components, implementing dark mode, or creating consistent design systems.
29json-ld-schemas
JSON-LD structured data for Organization, Person, Service, Product, FAQPage, and BreadcrumbList with reusable components. Use when implementing schema.org markup, adding rich snippets, or improving search engine understanding of page content.
22framer-motion-animations
Subtle animation patterns for hero sections, card reveals, page transitions, and scroll-triggered effects using Framer Motion. Use when adding animations to components, implementing scroll effects, or creating page transitions.
22responsive-mobile-first
Mobile-first responsive patterns with sticky headers, floating CTAs, accessible navigation, and touch-friendly interactions. Use when implementing responsive layouts, mobile navigation, or ensuring touch-friendly UI.
13