【Next.js】Server Componentsとは?Reactとの一番の違い

JavaScript

こんにちは、かつコーチです。

前回は loading.tsxerror.tsxnot-found.tsx を使って、状態ごとのUIを整える方法を解説しました。

ここまでで一通りルーティング周りは押さえられましたが、実はNext.jsのApp Routerには、これまでのReactの常識を変える大きな仕組みがあります。

それが今回解説するServer Componentsです。

Server Componentsとは?

サーバー側だけで動くコンポーネント

Server Components(サーバーコンポーネント)とは、ブラウザには送られず、サーバー側だけでレンダリングが完結するコンポーネントのことです。

これまでReactで作るコンポーネントは、基本的にすべてブラウザ側のJavaScriptとして動くClient Componentsでした。

App Routerでは、app ディレクトリ内のコンポーネントは何も書かなければデフォルトでServer Componentになります。

なぜそれが必要なのか

従来のReactアプリでは、データを取得する際に、まずJavaScriptをブラウザに送り、ブラウザ側で useEffect などを使ってAPIを叩く、という流れが一般的でした。

この方式は、JavaScriptのダウンロード・実行を待ってからデータ取得が始まるため、表示までに時間がかかりがちです。

Server Componentsを使うと、サーバー側でデータ取得とHTML生成まで済ませてからブラウザに送れるため、表示までの時間短縮ブラウザに送るJavaScriptの削減という2つのメリットが得られます。

基本の書き方

手順1:何もしなければServer Component

// app/products/page.tsx
async function getProducts() {
  const res = await fetch("https://api.example.com/products");
  return res.json();
}

export default async function ProductsPage() {
  const products = await getProducts();

  return (
    <ul>
      {products.map((product: { id: number; name: string }) => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}

page.tsx"use client" を書いていないため、これはServer Componentです。

コンポーネントの中で直接 await fetch() を書けるのが最大の特徴で、useEffectuseState を使ったデータ取得は不要になります。

手順2:Client Componentとの違いを比較する

項目Server ComponentsClient Components
実行場所サーバーのみサーバー(初回)+ブラウザ
useStateuseEffect使えない使える
onClick などのイベント使えない使える
データベース・APIキーへの直接アクセス可能不可(セキュリティ上NG)
ブラウザに送られるJS量増えないコンポーネント分だけ増える

つまずきやすい設定・注意点

Server Componentの中では、useStateonClick などブラウザに依存する機能は一切使えません。

インタラクティブな部分が必要な場合は、次回解説する "use client" を使ってClient Componentとして切り出す必要があります。

よくあるつまずきポイント・エラー対処

一次情報:Server Componentにボタンをつけて怒られた話

私が最初にServer Componentsを触ったとき、Reactの感覚のまま page.tsx に直接 onClick を書いてしまいました。

❌ Before:Server Componentにイベントハンドラを書く

// app/products/page.tsx("use client"なし)
export default async function ProductsPage() {
  const products = await getProducts();

  return (
    <ul>
      {products.map((product: { id: number; name: string }) => (
        <li key={product.id} onClick={() => alert(product.name)}>
          {product.name}
        </li>
      ))}
    </ul>
  );
}

このコードを実行すると、ビルド時に「Event handlers cannot be passed to Client Component props」というエラーが出て止まりました。

最初はエラーメッセージの意味が分からず戸惑いましたが、「onClick はブラウザで動く必要があるのに、このコンポーネントはサーバーでしか動かない」というのが原因でした。

✅ After:クリック部分だけClient Componentに切り出す

// app/products/ProductItem.tsx
"use client";

export function ProductItem({ name }: { name: string }) {
  return <li onClick={() => alert(name)}>{name}</li>;
}
// app/products/page.tsx(Server Componentのまま)
import { ProductItem } from "./ProductItem";

export default async function ProductsPage() {
  const products = await getProducts();

  return (
    <ul>
      {products.map((product: { id: number; name: string }) => (
        <ProductItem key={product.id} name={product.name} />
      ))}
    </ul>
  );
}

データ取得部分は page.tsx にServer Componentとして残し、クリックが必要な部分だけを ProductItem.tsx としてClient Componentに切り出すことで解決しました。

「インタラクティブな部分だけを小さく切り出す」という発想が、Server Components時代のReact設計で重要なポイントです。

まとめ

この記事のポイント

  • App Routerでは、app ディレクトリ内のコンポーネントはデフォルトでServer Component
  • Server Componentはサーバー側だけで動き、コンポーネント内で直接 await fetch() できる
  • useStateonClick などブラウザに依存する機能はServer Componentでは使えない
  • インタラクティブな部分は、小さいコンポーネントに切り出してClient Componentにする

次に読むべき記事

Server ComponentとClient Componentの違いが分かったところで、次は実際の判断基準を深掘りします。

次回は、"use client" はいつ・どこに書くべきか、使い分けの判断基準を解説します。

→ 次の記事:’use client’はいつ・どこに書く?使い分けの判断基準

タイトルとURLをコピーしました