nextjs
Next.js 16 App Router patterns and best practices for this project
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Next.js 16 App Router patterns and best practices for this project
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | nextjs |
| description | Next.js 16 App Router patterns and best practices for this project |
This project uses Next.js 16.1.4 with the App Router, TypeScript, and Tailwind CSS. Next.js 16 brings significant performance improvements with Turbopack as the default bundler and built-in React Compiler support.
Detected Version: Next.js 16.1.4 Router: App Router (default) Language: TypeScript 5.x Styling: Tailwind CSS 4.x Node.js: 20.9+ required
Key Files:
next.config.ts - Next.js configuration (TypeScript)src/app/layout.tsx - Root layout with metadatasrc/app/page.tsx - Home pagesrc/app/globals.css - Global stylestsconfig.json - TypeScript configuration with Next.js pluginsrc/app/
├── layout.tsx # Root layout (required)
├── page.tsx # Home route (/)
├── globals.css # Global styles
├── favicon.ico # Site favicon
└── tasks/ # Example nested route
└── page.tsx # /tasks route
| File | Purpose | Required |
|---|---|---|
layout.tsx | Shared UI for route segment | Yes (root) |
page.tsx | Route UI, makes route publicly accessible | Yes |
loading.tsx | Loading UI with Suspense | No |
error.tsx | Error UI boundary | No |
not-found.tsx | 404 UI | No |
By default, all components in the App Router are Server Components:
// src/app/page.tsx (Server Component by default)
export default function Home() {
// Runs on server only
// Can directly access backend resources
// Cannot use hooks or event handlers
return <div>Server-rendered content</div>;
}
Benefits:
Use 'use client' directive when you need:
useState, useEffect, etc.)onClick, onChange, etc.)'use client'; // Must be at the top of the file
import { useState } from 'react';
export default function TaskInput() {
const [value, setValue] = useState('');
return (
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}
// src/app/page.tsx (Server Component)
import TaskList from '@/components/TaskList'; // Client component
export default function Home() {
// Server logic here
return (
<main>
<h1>Task Manager</h1>
<TaskList /> {/* Client interactivity isolated here */}
</main>
);
}
Rule: Keep Server Components as far up the tree as possible, only use Client Components where interactivity is needed.
// src/app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Task Manager',
description: 'A simple task management app',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
Dynamic Metadata:
// src/app/tasks/[id]/page.tsx
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const task = await getTask(params.id);
return { title: task.description };
}
src/
├── app/ # App Router pages
│ ├── layout.tsx
│ ├── page.tsx
│ └── globals.css
├── components/ # React components
│ ├── ui/ # Generic UI components
│ ├── layout/ # Layout components
│ └── features/ # Feature-specific components
└── lib/ # Utilities and types
├── types.ts
└── utils.ts
Best Practice: Use @/ import alias (configured in tsconfig.json):
import { Task } from '@/lib/types';
import TaskInput from '@/components/TaskInput';
Next.js 16 includes automatic font optimization with next/font:
// src/app/layout.tsx
import { Geist, Geist_Mono } from 'next/font/google';
const geistSans = Geist({
variable: '--font-geist-sans',
subsets: ['latin'],
});
const geistMono = Geist_Mono({
variable: '--font-geist-mono',
subsets: ['latin'],
});
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable}`}>
{children}
</body>
</html>
);
}
import Image from 'next/image';
<Image
src="/task-icon.png"
alt="Task icon"
width={24}
height={24}
priority // For above-the-fold images
/>
// Global styles in src/app/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
// Component-level Tailwind classes
<div className="flex min-h-screen items-center justify-center bg-zinc-50">
<main className="max-w-3xl w-full px-4">
{/* Content */}
</main>
</div>
Turbopack is now the default bundler in Next.js 16:
Enable filesystem caching for even faster rebuilds:
// next.config.ts
export default {
experimental: {
turbopackFileSystemCacheForDev: true,
},
};
React Compiler is stable in Next.js 16:
useMemo, useCallback// No need for useMemo anymore - React Compiler handles it
const filteredTasks = tasks.filter(task => {
if (filter === 'active') return !task.completed;
if (filter === 'completed') return task.completed;
return true;
});
// Async components (Server Components)
export default async function TasksPage() {
const tasks = await fetchTasks(); // Direct async/await
return <TaskList tasks={tasks} />;
}
Before deploying:
npm run build to check for build errorsstrict: true in tsconfig.json)npm run build# Development server (Turbopack)
npm run dev
# Production build
npm run build
# Start production server
npm run start
# Linting
npm run lint
'use client';
export default function BadComponent() {
const data = await fetch('/api/tasks'); // ❌ async/await in Client Component
return <div>{data}</div>;
}
// Server Component
async function TasksPage() {
const tasks = await fetchTasks(); // ✅ Server-side data fetching
return <TaskList tasks={tasks} />; // Pass to Client Component
}
// Client Component
'use client';
function TaskList({ tasks }: { tasks: Task[] }) {
const [filter, setFilter] = useState('all'); // ✅ Client-side state
// ...
}
'use client'; // ❌ Unnecessary if no interactivity
export default function StaticContent() {
return <div>Just static content</div>;
}
// No 'use client' needed - Server Component by default
export default function StaticContent() {
return <div>Static content</div>; // ✅ Zero JS sent to client
}
// Server Component
import ClientComponent from './ClientComponent'; // Client Component
export default function Page() {
// ❌ Passing non-serializable data to Client Component
return <ClientComponent callback={() => console.log('test')} />;
}
// Server Component
export default function Page() {
// ✅ Pass serializable data only
return <ClientComponent data={{ id: 1, name: 'Task' }} />;
}