Next.js 16で本のコードが動かない!?paramsがPromiseになった変更点を解説


Next.js・microCMS 初心者ノート

Next.js 16で書籍のコードが動かない? paramsがPromiseになった変更点を解説

Next.js 16で書籍のコードが動かない?paramsがPromiseになった変更点を解説

Next.jsの書籍を見ながらコードを書いたのに、 params.slugの部分でエラーになってしまいました。
原因を調べてみると、Next.jsの新しいバージョンでは paramsがPromiseとして扱われるようになっていました。

Next.js 16 App Router params Promise 初心者向け
01

書籍どおりなのにコードが動かない

Next.jsとmicroCMSを使ったWebサイト制作を勉強するために、 書籍のサンプルコードを見ながら記事詳細ページを作っていました。

フォルダを作り、書籍に書かれているコードを入力して、 ブラウザーで記事ページを開いてみます。

ところが、画面には期待していた記事が表示されず、 ターミナルやエディターには paramsに関するエラーが表示されました。

?

書籍と同じように入力したはずなのに……

スペルを間違えたのかな?
フォルダ名が違うのかな?
microCMSの設定に問題があるのかな?

コードを何度見直しても、書籍の内容と大きな違いはありません。

そこでNext.jsの現在の仕様を調べたところ、 原因は入力ミスではなく、 書籍と現在のNext.jsとのバージョン差にありました。

✿

書籍のコードが動かないときは、 自分の入力だけでなく、書籍で使用しているNext.jsと 現在の環境のバージョン差も確認する必要があります。

今回問題になったのは、 App Routerの動的ルートで使用する paramsの扱い方です。

02

そもそもparamsとは?

paramsは、URLの一部から値を受け取るために使います。

例えば、microCMSで作ったブログに、 次のような記事詳細ページがあるとします。

URL https://example.com/blog/html-and-tsx

このURLでは、最後の html-and-tsxが記事を識別するための値です。

Next.jsのApp Routerでは、角括弧を使ったフォルダを作ることで、 URLによって内容が変わる動的ルートを作れます。

記事詳細ページのフォルダ構成
src
└── app
    └── blog
        └── [slug]
            └── page.tsx

[slug]の部分には、 実際のURLに含まれる値が入ります。

URL
paramsの中身
/blog/html-and-tsx
{ slug: "html-and-tsx" }
/blog/nextjs-basic
{ slug: "nextjs-basic" }
/blog/microcms-start
{ slug: "microcms-start" }
POINT

paramsは、URLの動的な部分を Next.jsのページ内で受け取るための値です。

03

Next.jsで何が変わったの?

Next.js 14以前では、 ページに渡されたparamsを 通常のオブジェクトとして直接使用できました。

以前 Next.js 14以前のイメージ
type Props = {
  params: {
    slug: string;
  };
};

export default function Page({ params }: Props) {
  const slug = params.slug;

  return <p>{slug}</p>;
}

この書き方では、paramsを受け取ったあと、 すぐにparams.slugへアクセスしています。

一方、現在のNext.jsでは、 paramsはPromiseとしてページへ渡されます。

現在 Next.js 16の書き方
type Props = {
  params: Promise<{
    slug: string;
  }>;
};

export default async function Page({
  params,
}: Props) {
  const { slug } = await params;

  return <p>{slug}</p>;
}
以前 params.slug
→
現在 const { slug } = await params

つまり、大きな変更点は次の3つです。

  • paramsの型を Promise<...>にする
  • ページ関数にasyncを付ける
  • await paramsで値を取り出す
注意

「Next.js 16で突然変わった」というより、 Next.js 15で導入された非同期化が、 Next.js 16でも正式な現在仕様として使われています。

04

Promiseとは?初心者向けにやさしく整理

paramsがPromiseになったと聞いても、 JavaScriptやTypeScriptを学び始めたばかりだと、 何が変わったのか分かりにくいかもしれません。

Promiseは簡単にいうと、 あとで受け取れる値を表す仕組みです。

1 Promiseを受け取る

まだ中身を直接使わない

›
2 awaitで待つ

値が使える状態になるまで待つ

›
3 中身を取り出す

slugを利用できる

例えば、次のコードでは getMessage()からPromiseが返ってきます。

Promiseの簡単な例
async function getMessage() {
  return "こんにちは";
}

async function showMessage() {
  const message = await getMessage();

  console.log(message);
}

awaitを付けることで、 Promiseの中から実際の値である 「こんにちは」を取り出しています。

paramsについても考え方は同じです。

paramsから値を取り出す
const resolvedParams = await params;
const slug = resolvedParams.slug;

分割代入を使うと、これを一行で書けます。

分割代入を使った書き方
const { slug } = await params;
✿

最初からPromiseの仕組みをすべて理解しなくても大丈夫です。
まずは「Promiseになったparamsは、 awaitしてから中身を使う」と覚えると整理しやすくなります。

05

なぜawait paramsが必要なの?

Promiseの状態で渡されたparamsは、 そのままでは通常のオブジェクトと同じように扱えません。

そのため、次のように直接プロパティへアクセスするのではなく、 先にPromiseを解決する必要があります。

直接アクセスする書き方
const slug = params.slug;
→
awaitしてから使う
const { slug } = await params;

awaitを使う関数には、 原則としてasyncを付けます。

asyncを付けたページ関数
export default async function Page({
  params,
}: Props) {
  const { slug } = await params;

  return <p>{slug}</p>;
}
async

関数内でawaitを使えるようにする

await

Promiseが解決されるまで待って値を取り出す

{ slug }

解決後のオブジェクトからslugだけを取り出す

06

書籍のコードを修正前・修正後で比較

実際にどこを変更すればよいのか、 記事詳細ページのコード全体で比較します。

修正前:paramsを同期オブジェクトとして扱う

修正前 app/blog/[slug]/page.tsx
type Props = {
  params: {
    slug: string;
  };
};

export default function BlogDetailPage({
  params,
}: Props) {
  return (
    <main>
      <h1>記事詳細ページ</h1>
      <p>記事ID:{params.slug}</p>
    </main>
  );
}

修正後:paramsをPromiseとして扱う

修正後 app/blog/[slug]/page.tsx
type Props = {
  params: Promise<{
    slug: string;
  }>;
};

export default async function BlogDetailPage({
  params,
}: Props) {
  const { slug } = await params;

  return (
    <main>
      <h1>記事詳細ページ</h1>
      <p>記事ID:{slug}</p>
    </main>
  );
}
1
paramsの型を変更する

{ slug: string }を Promise<{ slug: string }>へ変更します。

2
ページ関数にasyncを付ける

関数内でawaitを使用するために必要です。

3
await paramsで値を取得する

Promiseを解決してから、 slugを画面やAPI処理で使用します。

POINT

修正するのは、型だけではありません。 asyncとawaitも セットで追加する必要があります。

07

microCMSの記事詳細ページで使う例

microCMSの記事詳細ページでは、 URLから受け取ったslugや コンテンツIDを使って記事データを取得します。

ここでは、URLの値をmicroCMSのコンテンツIDとして使用する例を見てみます。

microCMSの記事詳細ページ
src
├── app
│   └── blog
│       └── [id]
│           └── page.tsx
└── libs
    └── microcms.ts

microCMSから記事を取得する関数

libs/microcms.ts
import { createClient } from "microcms-js-sdk";

export type Blog = {
  id: string;
  title: string;
  content: string;
};

const client = createClient({
  serviceDomain:
    process.env.MICROCMS_SERVICE_DOMAIN!,
  apiKey:
    process.env.MICROCMS_API_KEY!,
});

export const getBlogDetail = async (
  contentId: string
) => {
  return client.getListDetail<Blog>({
    endpoint: "blogs",
    contentId,
  });
};

paramsからIDを取得してAPIへ渡す

Next.js 16 app/blog/[id]/page.tsx
import { getBlogDetail } from "@/libs/microcms";

type Props = {
  params: Promise<{
    id: string;
  }>;
};

export default async function BlogDetailPage({
  params,
}: Props) {
  const { id } = await params;
  const article = await getBlogDetail(id);

  return (
    <main>
      <h1>{article.title}</h1>

      <div
        dangerouslySetInnerHTML={{
          __html: article.content,
        }}
      />
    </main>
  );
}
URL /blog/abc123
›
params { id: "abc123" }
›
microCMS 記事データを取得

このコードで重要なのは、 microCMSへIDを渡す前に await paramsを実行していることです。

初心者メモ

paramsそのものをmicroCMSへ渡すのではなく、 Promiseを解決して取り出した idやslugを渡します。

08

generateMetadataでも同じ対応が必要

記事ごとにページタイトルや説明文を変える場合、 Next.jsのgenerateMetadataを使用できます。

この関数でparamsを使う場合も、 Promiseとして受け取り、awaitする必要があります。

generateMetadataでparamsを使う
import type { Metadata } from "next";
import { getBlogDetail } from "@/libs/microcms";

type Props = {
  params: Promise<{
    id: string;
  }>;
};

export async function generateMetadata({
  params,
}: Props): Promise<Metadata> {
  const { id } = await params;
  const article = await getBlogDetail(id);

  return {
    title: article.title,
    description:
      `${article.title}の記事詳細ページです。`,
  };
}

export default async function BlogDetailPage({
  params,
}: Props) {
  const { id } = await params;
  const article = await getBlogDetail(id);

  return (
    <main>
      <h1>{article.title}</h1>
    </main>
  );
}

ページ本体だけを修正しても、 generateMetadata側で古い書き方が残っていると、 別の場所でエラーになる可能性があります。

  • page.tsxのページ関数
  • layout.tsxのレイアウト関数
  • generateMetadata
  • Route Handlerで動的ルートを使っている箇所
確認箇所

プロジェクト内でparams.を検索すると、 古い書き方が残っている場所を見つけやすくなります。

09

Client Componentではどうするの?

ここまで紹介したのは、 App RouterのServer Componentで paramsを受け取る書き方です。

Client Componentで現在の動的ルートを読みたい場合は、 useParamsを使用する方法があります。

Client ComponentでuseParamsを使う
"use client";

import { useParams } from "next/navigation";

export default function CurrentArticleId() {
  const params = useParams<{
    id: string;
  }>();

  return (
    <p>
      現在の記事ID:{params.id}
    </p>
  );
}
Server Component ページのpropsで受け取る const { id } = await params
Client Component フックで取得する const params = useParams()

microCMSから記事を取得してページを表示するだけであれば、 基本的にはServer Component側で await paramsを使う形が分かりやすいです。

10

修正しても動かないときの確認ポイント

await paramsへ修正してもエラーが残る場合は、 次の項目を確認します。

CHECK 01 ページ関数にasyncが付いているか

awaitを使う関数には、 asyncが必要です。

export default async function Page() {
  // awaitを使用できます
}
CHECK 02 paramsの型がPromiseになっているか

型だけが以前の書き方のままになっていないか確認します。

params: Promise<{
  slug: string;
}>;
CHECK 03 awaitより前にparamsを使っていないか

params.slugへ直接アクセスしている箇所が 残っていないか確認します。

const { slug } = await params;
CHECK 04 フォルダ名とプロパティ名が一致しているか

フォルダが[id]なら、 paramsのプロパティもidです。

// フォルダ:app/blog/[id]/page.tsx

type Props = {
  params: Promise<{
    id: string;
  }>;
};
CHECK 05 開発サーバーを再起動する

型や環境設定を変更したあとも表示が変わらない場合は、 開発サーバーを停止して起動し直します。

npm run dev
✿

エラーが表示されたら、コード全体を一度に書き換えるのではなく、 型、async、await、プロパティ名の順に 一つずつ確認すると原因を見つけやすくなります。

11

書籍が間違っているわけではない

書籍のサンプルコードが現在の環境で動かないと、 「本の内容が間違っているのでは?」と思ってしまうかもしれません。

しかし、技術書は執筆時点や検証時点のバージョンをもとに作られています。

Next.jsのように更新が続くフレームワークでは、 書籍の発売後に仕様が変わることがあります。

書籍の執筆時 当時のNext.jsで動作確認
›
バージョン更新 paramsが非同期化
›
現在の環境 await paramsへ修正

ページ構成やmicroCMSとの連携方法など、 書籍から学べる基本的な考え方はそのまま活用できます。

動かない部分だけを現在の仕様に合わせて修正することで、 書籍を学習教材として引き続き利用できます。

12

まとめ:paramsはawaitしてから使う

Next.jsの書籍を読みながら記事詳細ページを作っていると、 paramsの部分でコードが動かないことがあります。

その原因の一つが、 Next.jsのバージョンアップによる paramsの非同期化です。

今回覚えておきたいポイント

  • paramsは動的なURLの値を受け取るもの
  • 現在のApp Routerでは paramsがPromiseとして渡される
  • 型をPromise<...>に変更する
  • ページ関数へasyncを付ける
  • const { slug } = await paramsで値を取得する
  • generateMetadataなどでも同様に対応する
最後にこの形を覚えておきましょう
type Props = {
  params: Promise<{
    slug: string;
  }>;
};

export default async function Page({
  params,
}: Props) {
  const { slug } = await params;

  return <p>{slug}</p>;
}

書籍どおりに書いたコードが動かないと、 自分の理解や入力に問題があるように感じてしまいます。

けれど、今回のようにバージョン差が原因になっていることもあります。

エラーメッセージと現在の公式仕様を確認しながら修正することで、 コードを動かすだけでなく、 Next.jsの仕組みも少しずつ理解できるようになりました。

Next.js・microCMS初心者の学習記録 paramsはPromise。
asyncとawaitをセットで覚えておきます。

AILPENGINE。自動進化型AI-LP。公開後も、成果に合わせて進化する! 行動計測、分析、構成改善、成果最大化。自前計測、CTA位置最適化、レポート可視化。LP改善を相談する(相談無料)。

日本ドラフトのAIチャットボットで問い合わせ対応を効率化・自動化!24時間365日対応、簡単設定、マルチプラットフォーム対応で人件費・工数を削減。今すぐ無料で相談する

※当社は、当サイトに掲載する情報について、正確性・完全性・有用性その他一切の事項を保証するものではありません。
また、当サイトの情報をご利用いただいたことにより生じた損害、ならびに当サイトからリンクまたはバナー等を通じて移動した外部サイトにおいて提供される情報・サービス等により生じた損害について、当社は一切の責任を負いかねます。