Next.js チートシート

React フルスタックフレームワーク 実践リファレンス

Next.jsとは?

Next.jsはReactベースのフルスタックWebアプリケーションフレームワークです。サーバーサイドレンダリング(SSR)、静的サイト生成(SSG)、API Routes、Server Actionsなど、モダンなWeb開発に必要な機能を統合し、高速で最適化されたアプリケーションを簡単に構築できます。

App Router Server Components SSR/SSG/ISR TypeScript

プロジェクト作成

# create-next-app でプロジェクト作成
npx create-next-app@latest my-app

# 対話的セットアップ
# ✔ TypeScript? → Yes
# ✔ ESLint? → Yes
# ✔ Tailwind CSS? → Yes
# ✔ src/ directory? → Yes
# ✔ App Router? → Yes
# ✔ Import alias? → @/*

cd my-app
npm run dev  # http://localhost:3000

npm run dev - 開発サーバー起動

npm run build - 本番ビルド

npm start - 本番サーバー起動

App Router ディレクトリ構造

app/ フォルダ内の構造がURLに対応

app/
├── page.tsx            # / (ルート)
├── layout.tsx          # 全体レイアウト
├── about/
│   └── page.tsx        # /about
├── blog/
│   ├── page.tsx        # /blog
│   └── [slug]/
│       └── page.tsx    # /blog/:slug (動的)
└── api/
    └── users/
        └── route.ts    # /api/users (API Route)

page.tsx - ページコンポーネント

layout.tsx - レイアウト(共通UI)

[param] - 動的ルート

Server Components vs Client Components

Server Component(デフォルト)

// app/page.tsx
// サーバーでレンダリング(デフォルト)
async function getData() {
  const res = await fetch('https://api.example.com/data')
  return res.json()
}

export default async function Page() {
  const data = await getData()
  return 
{data.title}
}

✓ サーバー側で実行

✓ データフェッチング可能

✓ バンドルサイズ削減

✓ async/await 使用可

Client Component

// app/components/Counter.tsx
'use client'  // ← クライアントコンポーネント宣言

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)

  return (
    
  )
}

✓ ブラウザで実行

✓ イベントハンドラ使用可

✓ React Hooks 使用可

✓ ブラウザAPI利用可

データフェッチング & キャッシュ戦略

静的生成 (SSG)

// ビルド時にキャッシュ(デフォルト)
const res = await fetch(
  'https://api.example.com/data',
  { cache: 'force-cache' }
)

増分再生成 (ISR)

// 60秒ごとに再検証
const res = await fetch(
  'https://api.example.com/data',
  { next: { revalidate: 60 } }
)

動的レンダリング (SSR)

// リクエスト毎に取得
const res = await fetch(
  'https://api.example.com/data',
  { cache: 'no-store' }
)

On-Demand Revalidation: revalidatePath('/blog') または revalidateTag('posts') で手動再検証可能

API Routes & Server Actions

API Route (Route Handler)

// app/api/users/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  const users = [{ id: 1, name: 'Alice' }]
  return NextResponse.json(users)
}

export async function POST(request: Request) {
  const body = await request.json()
  // DB処理など
  return NextResponse.json({ success: true })
}

外部APIとして公開される(/api/users)

Server Action

// app/actions/user.ts
'use server'

export async function createUser(formData: FormData) {
  const name = formData.get('name')
  // DB処理など
  revalidatePath('/users')
  return { success: true }
}

// app/components/UserForm.tsx
'use client'
import { createUser } from '@/app/actions/user'

export default function UserForm() {
  return (
    
) }

フォーム専用。外部公開されない

動的ルート

// app/blog/[slug]/page.tsx
interface PageProps {
  params: { slug: string }
  searchParams: { [key: string]: string }
}

export default function BlogPost({ params }: PageProps) {
  return 

Post: {params.slug}

} // 静的生成用パス指定 export async function generateStaticParams() { const posts = await getPosts() return posts.map((post) => ({ slug: post.slug })) }

[slug] - 単一パラメータ

[...slug] - キャッチオールルート

[[...slug]] - オプショナルキャッチオール

メタデータ & SEO

// app/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'ページタイトル',
  description: 'ページ説明文',
  openGraph: {
    title: 'OGタイトル',
    description: 'OG説明文',
    images: ['/og-image.png'],
  },
}

// 動的メタデータ
export async function generateMetadata(
  { params }: PageProps
): Promise {
  const post = await getPost(params.slug)
  return {
    title: post.title,
    description: post.excerpt,
  }
}

画像最適化

import Image from 'next/image'

// 固定サイズ
Profile

// レスポンシブ(親要素に合わせる)
Banner
// 外部画像 External

フォント最適化

// app/layout.tsx
import { Inter, Noto_Sans_JP } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })
const notoSansJP = Noto_Sans_JP({
  weight: ['400', '700'],
  subsets: ['latin'],
  variable: '--font-noto-sans-jp',
})

export default function RootLayout({ children }) {
  return (
    
      
        {children}
      
    
  )
}

ミドルウェア

// middleware.ts (プロジェクトルート)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // 認証チェック例
  const token = request.cookies.get('token')

  if (!token && request.nextUrl.pathname.startsWith('/admin')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/admin/:path*', '/dashboard/:path*']
}

認証 (NextAuth.js v5)

// auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [GitHub],
})

// app/api/auth/[...nextauth]/route.ts
export { handlers as GET, handlers as POST }

// サーバーコンポーネントでセッション取得
import { auth } from "@/auth"

export default async function Page() {
  const session = await auth()
  if (!session) return 
Not authenticated
return
Hello {session.user?.name}
}

実務で役立つTips

環境変数

.env.local に記載。NEXT_PUBLIC_ プレフィックスでクライアント公開

パフォーマンス

dynamic('...') で動的インポート。loading.tsx でストリーミングUI

エラー処理

error.tsx でエラー境界。not-found.tsx で404ページ

デプロイ

Vercelへpush自動デプロイ。vercel.json で設定カスタマイズ

App Routerの特別なファイル

page.tsx

ページUI

layout.tsx

レイアウト

loading.tsx

ローディング

error.tsx

エラー境界

not-found.tsx

404ページ

template.tsx

再マウント型レイアウト

route.ts

API Route

default.tsx

並列ルートフォールバック

よく使うパターン

検索パラメータ取得

'use client'
import { useSearchParams } from 'next/navigation'

export default function SearchBar() {
  const searchParams = useSearchParams()
  const query = searchParams.get('q')

  return 
検索: {query}
}

プログラマティックナビゲーション

'use client'
import { useRouter } from 'next/navigation'

export default function LoginButton() {
  const router = useRouter()

  const handleLogin = async () => {
    // ログイン処理
    router.push('/dashboard')
    // または router.replace('/dashboard')
  }

  return 
}

サーバーサイドリダイレクト

import { redirect } from 'next/navigation'

export default async function Page() {
  const session = await getSession()

  if (!session) {
    redirect('/login')
  }

  return 
Protected Page
}

Suspenseでストリーミング

import { Suspense } from 'react'

export default function Page() {
  return (
    

ダッシュボード

Loading...
}>
) } async function SlowComponent() { const data = await fetchData() // 遅いデータ取得 return
{data}
}

next.config.js の主要設定

/** @type {import('next').NextConfig} */
const nextConfig = {
  // 外部画像ドメイン許可
  images: {
    domains: ['example.com', 'cdn.example.com'],
    // または remotePatterns で細かく制御
    remotePatterns: [
      {
        protocol: 'https',
        hostname: '**.example.com',
      },
    ],
  },

  // リダイレクト設定
  async redirects() {
    return [
      {
        source: '/old-path',
        destination: '/new-path',
        permanent: true,
      },
    ]
  },

  // リライト設定(プロキシ)
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'https://api.example.com/:path*',
      },
    ]
  },

  // 環境変数公開
  env: {
    CUSTOM_KEY: 'value',
  },

  // 静的エクスポート(SSG完全版)
  output: 'export',

  // Strict Mode
  reactStrictMode: true,
}

module.exports = nextConfig