Next.js 15 における `params` の型エラーと非同期化対応
はじめに
Next.js 15 環境で開発を行っていると、以下のような型エラーに遭遇することがあります。
Type error: Type '{ slug: string; }' is missing the following properties from type 'Promise<any>': then, catch, finally, \[Symbol.toStringTag]
これは Next.js 15 における App Router の仕様変更(breaking change) に起因しています。
具体的には、Next.js 15 では params
, searchParams
, cookies()
, headers()
などのいわゆる Request API がすべて 非同期(Promise
)として提供されるようになりました。
本記事では、この仕様変更の背景を整理し、サーバーコンポーネントとクライアントコンポーネントそれぞれにおける具体的な対応方法を解説します。
非同期化の概要(App Router 限定)
Next.js 15 では以下の Request API が Promise 化されました(公式ガイド:Async Request APIs - Next.js 15)。
params
searchParams
cookies()
headers()
draftMode()
これらはすべて Next.js が自動的に注入するパラメータであり、開発者が自ら定義する props に対して非同期化が適用されることはありません。
また、これらの API の非同期化は App Router 固有の挙動であり、従来の Pages Router(pages/
ディレクトリ)では一切影響を受けません。
修正前のコード(エラーが発生)
// app/blog/[slug]/page.tsx
type Props = {
params: { slug: string }; // ❌ 旧来の同期型
searchParams: { q?: string };
};
export default async function BlogPage({ params, searchParams }: Props) {
const { slug } = params;
const { q } = searchParams;
return <p>Slug: {slug}, Query: {q}</p>;
}
このコードは Next.js 14 以前では問題なく動作しますが、Next.js 15 では params
/ searchParams
が Promise
型となったため、型エラーが発生します。
修正後のコード(非同期化に対応)
type Props = {
params: Promise<{ slug: string }>;
searchParams: Promise<{ q?: string }>;
};
export default async function BlogPage({ params, searchParams }: Props) {
const { slug } = await params;
const { q } = await searchParams;
return <p>Slug: {slug}, Query: {q}</p>;
}
また、Next.js が公式に提供している Codemod を使用すれば、一括で修正可能です。
npx @next/codemod@canary next-async-request-api .
クライアントコンポーネントとの違い
page.tsx
, layout.tsx
など)
サーバーコンポーネント(-
params
,searchParams
はPromise
として渡されます - コンポーネント関数は
async
にし、await
で展開する必要があります
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params;
return <h1>{slug}</h1>;
}
"use client"
)
クライアントコンポーネント(React 19 以降では use()
を使用することで、非同期な値を簡潔に扱えます。
'use client'
import { use } from 'react'
export function ClientComponent({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = use(params);
return <p>{slug}</p>;
}
use()
は React 19 で導入された新 API で、Promise の中身を "同期的" に扱うための仕組みです。Suspense
と併用することでローディング制御も可能になります。
まとめ
- Next.js 15 では App Router において
params
/searchParams
などが非同期(Promise
)で提供されるようになった - 型エラーはこの非同期化を考慮せずに同期オブジェクトとして扱っていることが原因
- サーバーコンポーネントでは
await
、クライアントコンポーネントではuse()
を使って展開する - この挙動は App Router に限定され、Pages Router では従来どおり同期的に扱われる
Discussion