はじめに
引き続きWebの開発を進めています。
Next.jsを使用しているのですが、実装していてコンポーネントについての理解が足りないなと感じる場面があり、深追いするために色々と調べてみました。
そこで今回は備忘録も兼ねて、コンポーネントについてまとめてみようと思います。
1. ページコンポーネント (Page Components)
Next.jsでは、各ページはページコンポーネントとして定義されます。
-
app
ディレクトリでは、各ページはpage.tsx
として作成。 -
pages
ディレクトリを使用する場合は、デフォルトエクスポート。
export default function Home() {
return <h1>Home Page</h1>;
}
2. レイアウトコンポーネント (Layout Components)
ページ全体の共通レイアウトを定義するためのコンポーネントです。
ヘッダーやフッター、ナビゲーションなど、すべてのページで共通して適用される要素を管理するのに使用します。
App Router (app/layout.tsx
) の場合
Next.js 13以降のApp Routerでは、layout.tsx
を使用してレイアウトを作成します。
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<header>Header</header>
<main>{children}</main>
<footer>Footer</footer>
</div>
);
}
Pages Router (pages/_app.tsx
) の場合
pages/_app.tsx
を使うと、すべてのページに共通のレイアウトを適用できます。
import type { AppProps } from "next/app";
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<div>
<header>Header</header>
<main>
<Component {...pageProps} />
</main>
<footer>Footer</footer>
</div>
);
}
レイアウトの特徴
- layout.tsx はページの切り替え時に状態を維持できる
- _app.tsx はPages Routerで全ページに適用される
- ヘッダー、フッター、サイドバーなどを統一できる
3. サーバーコンポーネント (Server Components) [App Router]
Next.js 13以降のApp Router (app
ディレクトリ) では、デフォルトでコンポーネントがサーバーコンポーネント (Server Components) として動作します。
サーバーコンポーネントはサーバー側でのみ実行されるため、以下のような利点があります。
サーバーコンポーネントの特徴
- クライアント側にJavaScriptが送信されない(より軽量)
- データベースやAPIから直接データを取得できる
- 非同期関数 (
async/await
) を直接使える - SEOの最適化がしやすい
サーバーコンポーネントの基本
以下のように、async
関数としてAPIからデータを取得し、直接コンポーネントで利用できます。
// app/dashboard/page.tsx
async function Dashboard() {
const data = await fetch("https://api.example.com/data").then((res) => res.json());
return (
<div>
<h1>Dashboard</h1>
<p>{data.message}</p>
</div>
);
}
export default Dashboard;
クライアントコンポーネントとの違い
サーバーコンポーネントは useState
や useEffect
などのフックを使用できません。
なぜなら、サーバーコンポーネントはクライアント側で実行されないため、状態管理や副作用処理が不要だからです。
もし、クライアントサイドで状態を管理したい場合は、クライアントコンポーネントにする必要があります。
NG例(サーバーコンポーネントで useState を使用)
以下のコードはエラーになります。
export default function ServerComponent() {
const [count, setCount] = useState(0); // ❌ エラー
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
4. クライアントコンポーネント (Client Components) [App Router]
Next.js 13以降のApp Router (app
ディレクトリ) では、デフォルトでサーバーコンポーネントとして動作します。
しかし、"use client"
を追加することで、クライアントコンポーネントとして動作させることができます。
クライアントコンポーネントの特徴
useState
やuseEffect
などのReactフックを使用できる- イベントハンドリング(クリックやフォーム入力など)が可能
- ブラウザのAPI(
window
,localStorage
など)を使用できる - サーバーコンポーネントの中で組み込んで使うことができる
クライアントコンポーネントの基本
クライアントコンポーネントを作成するには、ファイルの先頭に "use client"
を記述 します。
"use client"; // クライアントコンポーネントにはこれが必須
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
);
}
5. ダイナミックコンポーネント (Dynamic Components)
Next.jsでは、ダイナミックインポートを使用することで、コンポーネントの遅延読み込み(Lazy Loading)が可能です。
これにより、不要なJavaScriptのロードを防ぎ、パフォーマンスを向上させることができます。
ダイナミックコンポーネントの特徴
- 初回レンダリング時に不要なコンポーネントを読み込まない
- ページロードを高速化できる
- SSR (サーバーサイドレンダリング) を無効化できる
- ユーザーアクションに応じてコンポーネントを読み込める
ダイナミックコンポーネントの基本
Next.jsの next/dynamic
を使用して、コンポーネントを遅延読み込みできます。
import dynamic from "next/dynamic";
// ダイナミックインポート
const HeavyComponent = dynamic(() => import("@/components/HeavyComponent"));
export default function Page() {
return (
<div>
<h1>ダイナミックコンポーネントの例</h1>
<HeavyComponent />
</div>
);
}
6. エラーバウンドリーコンポーネント (Error Boundary Components) [App Router]
Next.jsのエラーバウンドリーコンポーネントは、ページやコンポーネントで発生したランタイムエラーをキャッチし、適切に処理するための仕組みです。
app
ディレクトリを使用するApp Routerでは、エラーバウンドリーとして error.tsx
を作成することで、エラー発生時のUIをカスタマイズできます。
エラーバウンドリーの特徴
- コンポーネント内で発生したエラーをキャッチできる
- ユーザーに適切なエラーメッセージを表示できる
- ページごとにエラーハンドリングを設定できる
- 「再試行ボタン」などのカスタムUIを提供できる
エラーバウンドリーの基本
Next.jsの app
ディレクトリでは、ページやレイアウトのエラーをキャッチするために、error.tsx
を作成します。
エラーバウンドリーの基本的な構成
"use client"; // クライアントコンポーネントとして動作させる必要がある
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
return (
<div>
<h2>エラーが発生しました</h2>
<p>{error.message}</p>
<button onClick={() => reset()}>再試行</button>
</div>
);
}
7. ローディングコンポーネント (Loading Components) [App Router]
Next.jsのローディングコンポーネントを使用すると、ページやレイアウトのデータが読み込まれるまでの間にローディングUIを表示できます。
app
ディレクトリを使用するApp Routerでは、loading.tsx
を作成することで、ローディング中の状態を簡単に制御できます。
ローディングコンポーネントの特徴
- ページ遷移時にローディングアニメーションを表示
- サーバーコンポーネントでデータ取得中にスケルトンスクリーンを表示
- ページごと、またはレイアウト全体に適用可能
- 自動的に適用され、追加のコードは不要
基本的な loading.tsx
の作成
Next.jsの app
ディレクトリでは、ページやレイアウトと同じディレクトリに loading.tsx
を作成すると、そのページやレイアウトのローディング時に自動で表示されます。
app/loading.tsx
(全ページ共通のローディングUI)
export default function Loading() {
return <p>Loading...</p>;
}
8. テンプレートコンポーネント (Template Components) [App Router]
Next.jsのテンプレートコンポーネント (Template Components) を使用すると、
ページ遷移時にレイアウトを維持しつつ、一部のコンテンツのみを再レンダリングすることが可能になります。
これは、Reactの React.memo
のような最適化をNext.jsのレベルで実現する機能です。
テンプレートコンポーネントの特徴
- ページ遷移時にレイアウトを再レンダリングせずに維持できる
- レイアウトの状態 (スクロール位置や開いたメニューなど) を保持可能
- ページごとに異なる
template.tsx
を設定できる layout.tsx
とは異なり、子コンポーネントの更新を最適化できる
テンプレートコンポーネントの基本
Next.jsの app
ディレクトリでは、template.tsx
を作成することで、テンプレートコンポーネントを適用できます。
配置例:
app/dashboard/template.tsx
export default function DashboardTemplate({ children }: { children: React.ReactNode }) {
return (
<div className="dashboard-template">
<h1>ダッシュボード</h1>
{children}
</div>
);
}
9. ミドルウェアコンポーネント (Middleware Components)
Next.jsのミドルウェアコンポーネント (Middleware Components) は、
リクエストとレスポンスの間で処理を行い、リクエストのリダイレクトや認証チェックを実装する機能です。
middleware.ts
を作成することで、グローバルまたは特定のルートに適用できます。
ミドルウェアの特徴
- リクエストがサーバーに到達する前に処理を実行できる
- 認証チェックやリダイレクトを実装できる
- APIエンドポイントやページのアクセス制御が可能
- Edge Functions を活用して超高速に動作
ミドルウェアの基本
Next.jsでミドルウェアを使用するには、プロジェクトのルートに middleware.ts
を作成します。
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// ミドルウェアの処理
export function middleware(req: NextRequest) {
const token = req.cookies.get("auth-token");
// トークンがない場合はログインページへリダイレクト
if (!token) {
return NextResponse.redirect(new URL("/login", req.url));
}
return NextResponse.next();
}
まとめ
Next.jsには、さまざまな種類のコンポーネントがあり、それぞれ異なる用途で活用できます。
適切に使い分けることで、アプリのパフォーマンスやUXを最適化できます。
各コンポーネントの用途一覧
コンポーネント名 | 役割 |
---|---|
ページコンポーネント | 各ページ (page.tsx ) のルートコンポーネント |
レイアウトコンポーネント | 共通レイアウト (layout.tsx ) を定義 |
サーバーコンポーネント | サーバーサイドでデータを取得&レンダリング |
クライアントコンポーネント | クライアントサイドで状態管理やイベント処理 |
ダイナミックコンポーネント | 必要なときにのみコンポーネントを読み込む |
エラーバウンドリー | エラー発生時に適切な処理を行う (error.tsx ) |
ローディングコンポーネント | データ取得中のUI (loading.tsx ) を制御 |
テンプレートコンポーネント | ページ遷移時にレイアウトを維持 (template.tsx ) |
ミドルウェア | リクエスト時に認証・リダイレクトなどを処理 |
どのコンポーネントをどのタイミングで使うべきか?
目的 | 適切なコンポーネント |
---|---|
ページごとのルートコンポーネント | ページコンポーネント (page.tsx ) |
全ページ共通のヘッダー・フッター | レイアウトコンポーネント (layout.tsx ) |
データ取得 & SEO対策 | サーバーコンポーネント |
ユーザーの操作 (クリック・入力) | クライアントコンポーネント |
初回ロードを速くする | ダイナミックコンポーネント |
エラーハンドリングを統一 | エラーバウンドリー |
データ取得中のUIを表示 | ローディングコンポーネント |
ページ遷移時にレイアウトを維持 | テンプレートコンポーネント |
認証チェック & リダイレクト | ミドルウェア |
まとめ
- Next.jsには、サーバーコンポーネント・クライアントコンポーネントの区別がある
- ページの共通レイアウトには
layout.tsx
を使用 - エラーやローディングのUIをカスタマイズできる (
error.tsx
,loading.tsx
) - ミドルウェアを使うことで、リクエスト時に認証やリダイレクトを処理
- 適切なコンポーネントを選ぶことで、パフォーマンスとUXを向上を期待できる
さいごに
何とか減量目標の3分の1を達成しました、この調子で健康的な生活を取り戻します!