Skip to content

Next.js 16の新機能:React 20コンパイラーとRoute Middleware

Next.js 16は2025年初頭にリリースされ、今回のアップデートで最も注目すべきはReact 20コンパイラーのデフォルト有効化Route Middlewareの刷新、そしてTransactional Server Actionsです。

注:本記事はNext.js 16 RCに基づいており、正式リリース時に一部変更となる場合があります。

React 20コンパイラーのデフォルト有効化

Next.js 15はReact Compilerをexperimentalオプションとして導入しました。Next.js 16ではデフォルトで有効になります。

javascript
// next.config.js — 16 ではデフォルトで有効(設定不要)
/** @type {import('next').NextConfig} */
const nextConfig = {
  // 15 では experimental.reactCompiler: true が必要だったが
  // 16 では設定不要
};

export default nextConfig;

Compilerが有効になると、手動メモ化の多くは不要になります:

typescript
// 以前:useMemo と useCallback を多用
function ProductList({ products, onSelect }: Props) {
  const sortedProducts = useMemo(
    () => [...products].sort((a, b) => a.price - b.price),
    [products]
  );

  const handleSelect = useCallback(
    (id: string) => {
      onSelect(id);
    },
    [onSelect]
  );

  return (
    <ul>
      {sortedProducts.map((p) => (
        <ProductItem key={p.id} product={p} onSelect={handleSelect} />
      ))}
    </ul>
  );
}

// React 20 Compiler 有効時:コンパイラーが自動でメモ化を挿入
// useMemo / useCallback を書かなくても同等のパフォーマンスになる
function ProductList({ products, onSelect }: Props) {
  const sortedProducts = [...products].sort((a, b) => a.price - b.price);

  return (
    <ul>
      {sortedProducts.map((p) => (
        <ProductItem
          key={p.id}
          product={p}
          onSelect={(id) => onSelect(id)}
        />
      ))}
    </ul>
  );
}

Route Middleware の刷新

Next.js 16のRoute Middlewareはmatcher設定が強化されました。

typescript
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { verifyToken } from "@/lib/auth";

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 認証チェック
  if (pathname.startsWith("/dashboard")) {
    const token = request.cookies.get("auth-token")?.value;

    if (!token) {
      return NextResponse.redirect(new URL("/login", request.url));
    }

    const payload = await verifyToken(token);
    if (!payload) {
      return NextResponse.redirect(new URL("/login", request.url));
    }

    // 16 の新機能:リクエストヘッダーを通じてページコンポーネントにデータを渡す
    const requestHeaders = new Headers(request.headers);
    requestHeaders.set("x-user-id", payload.userId);
    requestHeaders.set("x-user-role", payload.role);

    return NextResponse.next({
      request: { headers: requestHeaders },
    });
  }

  return NextResponse.next();
}

export const config = {
  matcher: [
    // 16 の新機能:否定先読みを使ったマッチャー
    "/((?!api|_next/static|_next/image|favicon.ico).*)",
  ],
};

Transactional Server Actions

これはNext.js 16で最も期待される新機能です——複数のServer Actionをトランザクションとしてグループ化し、どれか1つが失敗した場合はすべてロールバックします。

typescript
// app/checkout/actions.ts
"use server";

import { transaction } from "next/server"; // 新 API
import { db } from "@/lib/db";
import { stripe } from "@/lib/stripe";
import { revalidatePath } from "next/cache";

export async function checkoutAction(cartId: string, paymentMethodId: string) {
  // transaction() でラップされた操作はアトミックになる
  return transaction(async (tx) => {
    // 1. 在庫チェックと予約
    const cart = await tx.cart.findUnique({
      where: { id: cartId },
      include: { items: { include: { product: true } } },
    });

    if (!cart) throw new Error("カートが見つかりません");

    // 在庫をチェックして減らす
    for (const item of cart.items) {
      const product = await tx.product.update({
        where: { id: item.productId },
        data: { stock: { decrement: item.quantity } },
      });

      if (product.stock < 0) {
        // トランザクションをロールバックして在庫を元に戻す
        throw new Error(`${product.name} の在庫が不足しています`);
      }
    }

    // 2. 注文作成
    const order = await tx.order.create({
      data: {
        userId: cart.userId,
        items: {
          create: cart.items.map((item) => ({
            productId: item.productId,
            quantity: item.quantity,
            price: item.product.price,
          })),
        },
        total: cart.items.reduce(
          (sum, item) => sum + item.quantity * item.product.price,
          0,
        ),
      },
    });

    // 3. 決済処理(失敗すると在庫と注文が自動ロールバック)
    const paymentIntent = await stripe.paymentIntents.create({
      amount: order.total * 100,
      currency: "jpy",
      payment_method: paymentMethodId,
      confirm: true,
    });

    // 4. 注文ステータス更新
    await tx.order.update({
      where: { id: order.id },
      data: {
        status: "PAID",
        paymentIntentId: paymentIntent.id,
      },
    });

    revalidatePath("/orders");
    return { orderId: order.id };
  });
}

クライアントコンポーネントでの使用:

typescript
// app/checkout/checkout-form.tsx
"use client";

import { useActionState } from "react";
import { checkoutAction } from "./actions";

export function CheckoutForm({ cartId }: { cartId: string }) {
  const [state, action, isPending] = useActionState(checkoutAction, null);

  return (
    <form action={action.bind(null, cartId)}>
      <PaymentElement />

      {state?.error && (
        <div className="error">{state.error}</div>
      )}

      <button type="submit" disabled={isPending}>
        {isPending ? "処理中..." : "注文を確定する"}
      </button>
    </form>
  );
}

まとめ

Next.js 16の3つのコアアップデートはいずれも直接的な開発体験の改善をもたらします——Compilerでメモ化の手間を省き、Transactional Server Actionsでデータの一貫性を保証し、Route Middlewareで認証ロジックを集中管理できます。既存のNext.js 15プロジェクトへのアップグレードは比較的スムーズで、特に破壊的変更はありません。

MIT Licensed