公開日:2025年10月22日(最終更新日:2025年10月23日)
• Next.jsの新しいバージョンの変更点について(2025年10月記載) • 変更点 1(インストール時の質問) • 変更点 2(middleware.jsからproxy.jsへ) • 変更点 3(contextの使い方) ――――――――――――― • 参考情報 1:Reactサーバーコンポーネントの解説 • 参考情報 2:Reactサーバーコンポーネントを使ったデータ取得の方法 • 参考情報 3:ReactサーバーコンポーネントとReactバージョン19を使ったコードの書き方
本書は発行時点(2024年9月)での最新バージョンのNext.js(バージョン14)をベースに解説を進めています。しかし、Next.jsは新しいバージョンが定期的にリリースされており、現時点(2025年10月)での最新版はバージョン16です。そのため、本書の説明の通りに下記コマンドでNext.jsをインストールすると、自動でバージョン16がインストールされます。
npx create-next-app next-market
バージョン15および16では多くの変更が加えられたため、バージョン14を使っている本書の通りに進めるとエラーの出る箇所があります。本ページでは以下、変更点と対応策を紹介していきます。
なお、Next.jsバージョン16を使った最終完成見本コードは下記URLにあるので、適宜参考にしてください。
• https://github.com/mod728/nextjs-react-book-nextjs-version-16
最新のバージョン16では、上記Next.jsインストールコマンド実行時の質問が大きく変更されています。
本書Chapter 2の「01 Next.jsのインストール」におけるNext.jsのインストール部分の解説は、次のもので読み替えて進めてください。
......それではここ(ダウンロードフォルダ)にNext.jsをインストールしましょう。次のコマンドをターミナルに入力し、「Enter」キーで実行してください。
% npx create-next-app next-market
この「npx」とは、インストールを実行する特別なコマンドで、本書ではここでしか使いません。次の「create-next-app」では、Next.jsのインストールを指定しています。最後の「next-market」はインストールしたフォルダに付ける名前で、ここでは「next-market」としていますが、好きなもので大丈夫です。
ここで次のようなメッセージが出ることがありますが、特に問題ではないので、何もせずに「Enter」キーを押してください。
Need to install the following packages:
create-next-app@16.0.0
Ok to proceed? (y)
インストールを実行すると質問がいくつか出てきます。一つ目は、インストールするNext.jsの初期設定をどの程度済ませておくか問う質問です。
? Would you like to use the recommended Next.js defaults? › - Use arrow-keys. Return to submit.
Yes, use recommended defaults
No, reuse previous settings
❯ No, customize settings
Choose your own preferences
「TypeScriptやTailwind CSSなども一緒にインストールする(use recommended defaults)」、「前回の設定の再利用(reuse previous settings)」、「自分で決める(customize settings)」という3つの選択肢があります。本書ではミニマムな設定で進めていきたいので、矢印キーで「No, customize settings」を選び、「Enter」キーで実行してください。
次に、Next.jsの初期設定を決める次のような質問が出てきます(2025年10月現在)。
? Would you like to use TypeScript? › No / Yes
? Which linter would you like to use? › - Use arrow-keys. Return to submit.
? Would you like to use Tailwind CSS? › No / Yes
? Would you like to use `src/` directory? › No / Yes
? Would you like to use App Router? (recommended) › No / Yes
? Would you like to customize the default import alias (@/*)? › No / Yes
「Would you like to〜」とは「Do you want〜」の丁寧な聞き方なので、これらの質問は「TypeScriptやTailwind CSSなども一緒にインストールしますか?」と聞いているのだとわかります。
本書では最小限のデフォルト設定で開発を進めたいので、各質問には次のように回答してください。
✔ Would you like to use TypeScript? → 「No」
✔ Which linter would you like to use? → 「None」
✔ Would you like to use React Compiler? → 「Yes」
✔ Would you like to use Tailwind CSS? → 「No」
✔ Would you like your code inside a `src/` directory? → 「No」
✔ Would you like to use App Router? (recommended) → 「Yes」
✔ Would you like to use Turbopack? (recommended) → 「Yes」
✔ Would you like to customize the import alias (`@/*` by default)? → 「No」
注意して欲しいのは下から3つ目の「App Router」に関する質問です。かならず「Yes」を選択してください。これによって、Next.jsバージョン13以降でデフォルトとなっている「Appフォルダ」が利用できます。
なお、TurbopackとはNext.jsの開発元Vercel社が開発を主導しているバンドラー(開発を高速化するツール)で、バージョン16からはNext.js開発時のデフォルトのバンドラーになっています。またReact Compilerは、Reactのコードを最適化するツールで、これもNext.jsバージョン16から利用できるようになっています。本書ではインストールしてもしなくても構いませんが、今後はReact Compilerの利用が標準になっていくと考えられるので、ここでは「Yes」を選択しています。
本書Chapter 4の「04 ユーザーのログイン状態を判定する機能」の項以降、middleware.jsというファイルが登場します。
バージョン16ではファイル名がmiddleware.jsからproxy.jsに変更されたので、proxy.jsという名前でファイルを作ってください。
またそれに合わせて、ファイル内のコードでmiddleware()と書いてある箇所もproxy()に変更してください。
import { NextResponse } from "next/server"
export async function proxy(){ // 「middleware()」から「proxy()」に変更
return NextResponse.next()
}
本書ではバックエンドとフロントエンド両方において、contextというコードを複数の箇所で使っています。Next.jsバージョン16ではcontextの使い方が変更されており、本書の通りに進めるとエラーが発生するので、これから説明する変更をコードに加えてください。
本書ではcontextをURL取得のために使っています。最初に登場するのは、Chapter 3のMongoDBからアイテムを1つ読み取る機能を開発する時です。
本書で使っているバージョン14では、contextの内部へはcontext.params.idのようにダイレクトにアクセスできました。しかしバージョン16ではawaitを使う必要があるため、次のように1行余分に書く必要があります。
// バージョン14(本書)
console.log(context.params.id)
// バージョン16
const params = await context.params
console.log(params.id)
contextの利用時には上記の書き方で対応してください。
以下、本書を終えた時点での完成見本コードを用いて、バージョン16を使った場合に変更すべき箇所を紹介します。変更後のコードは下記URLにあるので、適宜参考にしながら進めてください。
• https://github.com/mod728/nextjs-react-book-nextjs-version-16
まずはバックエンド関係のファイルからです。
// app/api/item/readSingle/[id]/route.js(アイテムをひとつ読み取る機能)
import { NextResponse } from "next/server"
import connectDB from "../../../../utils/database"
import { ItemModel } from "../../../../utils/schemaModels"
export async function GET(request, context){
try{
await connectDB()
const params = await context.params // 追加
const singleItem = await ItemModel.findById(params.id) // 変更
return NextResponse.json({message: "アイテム読み取り成功(シングル)", singleItem: singleItem})
}catch{
...
// app/api/item/update/[id]/route.js(アイテムの編集機能)
import { NextResponse } from "next/server"
import connectDB from "../../../../utils/database"
import { ItemModel } from "../../../../utils/schemaModels"
export async function PUT(request, context){
const reqBody = await request.json()
try{
await connectDB()
const params = await context.params // 追加
const singleItem = await ItemModel.findById(params.id) // 変更
if(singleItem.email === reqBody.email){
await ItemModel.updateOne({_id: params.id}, reqBody) // 変更
return NextResponse.json({message: "アイテム編集成功"})
}else{
return NextResponse.json({message: "他の人が作成したアイテムです"})
}
}catch{
...
// app/api/item/delete/[id]/route.js(アイテムの削除機能)
import { NextResponse } from "next/server"
import connectDB from "../../../../utils/database"
import { ItemModel } from "../../../../utils/schemaModels"
export async function DELETE(request, context){
const reqBody = await request.json()
try{
await connectDB()
const params = await context.params // 追加
const singleItem = await ItemModel.findById(params.id) // 変更
if(singleItem.email === reqBody.email){
await ItemModel.deleteOne({_id: params.id}) // 変更
return NextResponse.json({message: "アイテム削除成功"})
}else{
return NextResponse.json({message: "他の人が作成したアイテムです"})
}
}catch{
...
次はフロントエンド関係のファイルです。
// app/item/readSingle/[id]/page.js(アイテムをひとつ読み取るページ)
import Image from "next/image"
import Link from "next/link"
const getSingleItem = async(id) => {
...
}
const ReadSingleItem = async(context) => {
const params = await context.params // 追加
const singleItem = await getSingleItem(params.id) // 変更
return (
<div className="grid-container-si">
...
// app/item/update/[id]/page.js(アイテム編集ページ)
"use client"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import useAuth from "../../../utils/useAuth"
const UpdateItem = (context) => {
...
useEffect(() => {
const getSingleItem = async() => { // 「id」を削除
const params = await context.params // 追加 // ⬇変更
const response = await fetch(`${process.env.NEXT_PUBLIC_URL}/api/item/readsingle/${params.id}`, {cache: "no-store"})
const jsonData = await response.json()
const singleItem = jsonData.singleItem
setTitle(singleItem.title)
setPrice(singleItem.price)
setImage(singleItem.image)
setDescription(singleItem.description)
setEmail(singleItem.email)
setLoading(true)
}
getSingleItem() // 「context.params.id」を削除
}, [context])
const handleSubmit = async(e) => {
e.preventDefault()
const params = await context.params // 追加
try{ // ⬇変更
const response = await fetch(`${process.env.NEXT_PUBLIC_URL}/api/item/update/${params.id}`, {
method: "PUT",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": `Bearer ${localStorage.getItem("token")}`
},
...
// app/item/delete/[id]/page.js(アイテム削除ページ)
"use client"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import Image from "next/image"
import useAuth from "../../../utils/useAuth"
const DeleteItem = (context) => {
...
useEffect(() => {
const getSingleItem = async() => { // 「id」を削除
const params = await context.params // 追加 // ⬇変更
const response = await fetch(`${process.env.NEXT_PUBLIC_URL}/api/item/readsingle/${params.id}`, {cache: "no-store"})
const jsonData = await response.json()
const singleItem = jsonData.singleItem
setTitle(singleItem.title)
setPrice(singleItem.price)
setImage(singleItem.image)
setDescription(singleItem.description)
setEmail(singleItem.email)
setLoading(true)
}
getSingleItem() // 「context.params.id」を削除
}, [context])
const handleSubmit = async(e) => {
e.preventDefault()
const params = await context.params // 追加
try{ // ⬇変更
const response = await fetch(`${process.env.NEXT_PUBLIC_URL}/api/item/delete/${params.id}`, {
method: "DELETE",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": `Bearer ${localStorage.getItem("token")}`
},
...
Next.jsではReactサーバーコンポーネントがデフォルトのReactコンポーネントになっています。ビギナー向けの本書では深い解説を避けていますが、くわしく知りたい方は下記ページをご覧ください。
Reactサーバーコンポーネントを使うと、データの読み取りに関しては、APIサーバーを経由せずにデータベースに直接アクセスをして実行することが可能です。
興味のある方は下記ページをご覧ください。
• Reactサーバーコンポーネントを使ったデータ取得の方法
フロントエンド側の編集ページと削除ページを、ReactサーバーコンポーネントおよびReactバージョン19を使って書く方法を参考情報として公開しました。
興味のある方は下記ページをご覧ください。Chapter 8終了時のコードに追加する形で進めています。