paramsって何?
params(パラムス)は、URLから値を取り出すための特別な箱です。
例えば、こんなURLがあったとします:
https://example.com/products/123
この123
の部分を取り出して使いたい時に、params
を使います。
身近な例で理解しよう
YouTubeの動画ページ
https://youtube.com/watch?v=abc123xyz
-
abc123xyz
が動画のID - YouTubeはこのIDを使って、どの動画を表示するか決める
Amazonの商品ページ
https://amazon.co.jp/dp/B08N5WRWNW
-
B08N5WRWNW
が商品のID - AmazonはこのIDを使って、商品情報を取得する
Next.jsのparams
も同じことをします!
なぜparamsが必要なの?
問題:同じようなページがたくさんある
例えば、商品が1000個あるECサイトを作るとします。
❌ paramsを使わない場合(大変!)
products/1.jsx → 商品1のページ
products/2.jsx → 商品2のページ
products/3.jsx → 商品3のページ
...
products/1000.jsx → 商品1000のページ
1000個もファイルを作るの?無理ですよね。
✅ paramsを使う場合(簡単!)
products/[id].jsx → 全商品に対応
たった1つのファイルで、全ての商品ページを作れます!
paramsの基本的な使い方
1. フォルダ構成
角カッコ[ ]
を使ってフォルダを作ります。
app/
products/
[id]/
page.jsx ← このファイルで全商品に対応
ポイント:
-
[id]
の部分が「変わる値」を表す -
id
は好きな名前でOK([productId]
、[slug]
など)
2. コードで使う
// app/products/[id]/page.jsx
export default function ProductPage({ params }) {
return (
<div>
<h1>商品ID: {params.id}</h1>
<p>この商品の詳細ページです</p>
</div>
);
}
URLとparamsの対応:
-
/products/1
にアクセス →params.id = "1"
-
/products/999
にアクセス →params.id = "999"
-
/products/abc
にアクセス →params.id = "abc"
実際の使用例
例1:商品詳細ページ
// app/products/[id]/page.jsx
export default async function ProductPage({ params }) {
// paramsからIDを取り出す
const productId = params.id;
// そのIDで商品データを取得
const response = await fetch(`https://api.shop.com/products/${productId}`);
const product = await response.json();
return (
<div>
<h1>{product.name}</h1>
<p>商品番号: {productId}</p>
<p>価格: {product.price}円</p>
<p>{product.description}</p>
</div>
);
}
動作例:
-
/products/123
→ 商品123の情報を表示 -
/products/456
→ 商品456の情報を表示
例2:ユーザープロフィールページ
// app/users/[username]/page.jsx
export default async function UserPage({ params }) {
const username = params.username;
const response = await fetch(`https://api.example.com/users/${username}`);
const user = await response.json();
return (
<div>
<h1>@{username}</h1>
<p>名前: {user.name}</p>
<p>自己紹介: {user.bio}</p>
<p>フォロワー: {user.followers}人</p>
</div>
);
}
動作例:
-
/users/taro
→ taroさんのプロフィール -
/users/hanako
→ hanakoさんのプロフィール
例3:ブログ記事ページ
// app/blog/[slug]/page.jsx
export default async function BlogPost({ params }) {
const slug = params.slug;
const response = await fetch(`https://api.blog.com/posts/${slug}`);
const post = await response.json();
return (
<article>
<h1>{post.title}</h1>
<p>投稿日: {post.publishedAt}</p>
<p>著者: {post.author}</p>
<div>{post.content}</div>
</article>
);
}
動作例:
-
/blog/my-first-post
→ "my-first-post"の記事 -
/blog/hello-world
→ "hello-world"の記事
複数のparamsを使う
ネストされたパラメータ
2つ以上のパラメータを使うこともできます。
フォルダ構成:
app/
shop/
[category]/
[productId]/
page.jsx
コード:
// app/shop/[category]/[productId]/page.jsx
export default async function ProductPage({ params }) {
const category = params.category;
const productId = params.productId;
return (
<div>
<p>カテゴリ: {category}</p>
<p>商品ID: {productId}</p>
</div>
);
}
URLとparamsの対応:
-
/shop/electronics/123
params.category = "electronics"
params.productId = "123"
-
/shop/books/456
params.category = "books"
params.productId = "456"
実用例:カテゴリ別商品ページ
// app/shop/[category]/[productId]/page.jsx
export default async function ProductPage({ params }) {
const { category, productId } = params;
const response = await fetch(
`https://api.shop.com/${category}/products/${productId}`
);
const product = await response.json();
return (
<div>
<nav>
<a href={`/shop/${category}`}>← {category}に戻る</a>
</nav>
<h1>{product.name}</h1>
<p>カテゴリ: {category}</p>
<p>価格: {product.price}円</p>
<button>購入する</button>
</div>
);
}
URLの例:
/shop/electronics/smartphone-123
/shop/books/novel-456
/shop/fashion/shoes-789
paramsの型と注意点
paramsは常に文字列
重要:paramsの値は必ず文字列(string)です。
export default function Page({ params }) {
console.log(params.id); // "123" (文字列)
console.log(typeof params.id); // "string"
}
数値として使いたい場合
export default async function Page({ params }) {
// 文字列を数値に変換
const id = Number(params.id);
// または
const id = parseInt(params.id, 10);
console.log(id); // 123 (数値)
console.log(typeof id); // "number"
// 数値として計算できる
const nextId = id + 1; // 124
}
例:ページネーション
// app/posts/page/[pageNumber]/page.jsx
export default async function PostsPage({ params }) {
// 文字列を数値に変換
const pageNumber = parseInt(params.pageNumber, 10);
// 1ページあたり10件
const postsPerPage = 10;
const skip = (pageNumber - 1) * postsPerPage;
const response = await fetch(
`https://api.blog.com/posts?skip=${skip}&limit=${postsPerPage}`
);
const posts = await response.json();
return (
<div>
<h1>記事一覧 - ページ {pageNumber}</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
<nav>
<a href={`/posts/page/${pageNumber - 1}`}>← 前のページ</a>
<a href={`/posts/page/${pageNumber + 1}`}>次のページ →</a>
</nav>
</div>
);
}
paramsのバリデーション(検証)
不正な値が来た時の対処も大切です。
例1:数値チェック
export default async function ProductPage({ params }) {
const id = params.id;
// 数値かどうかチェック
if (!/^\d+$/.test(id)) {
return (
<div>
<h1>エラー</h1>
<p>商品IDは数値である必要があります</p>
</div>
);
}
// 正常処理
const response = await fetch(`https://api.shop.com/products/${id}`);
const product = await response.json();
return <div>{product.name}</div>;
}
例2:存在チェック
export default async function UserPage({ params }) {
const username = params.username;
try {
const response = await fetch(`https://api.example.com/users/${username}`);
// 404エラーの場合
if (response.status === 404) {
return (
<div>
<h1>ユーザーが見つかりません</h1>
<p>@{username} は存在しません</p>
</div>
);
}
const user = await response.json();
return <div>@{username}のページ</div>;
} catch (error) {
return <div>エラーが発生しました</div>;
}
}
例3:許可された値のみ受け付ける
// app/docs/[section]/page.jsx
export default function DocsPage({ params }) {
const allowedSections = ['getting-started', 'api', 'examples', 'faq'];
// 許可されたセクションかチェック
if (!allowedSections.includes(params.section)) {
return (
<div>
<h1>ページが見つかりません</h1>
<p>利用可能なセクション:</p>
<ul>
{allowedSections.map(section => (
<li key={section}>
<a href={`/docs/${section}`}>{section}</a>
</li>
))}
</ul>
</div>
);
}
return <div>{params.section}のドキュメント</div>;
}
paramsを使った実践パターン
パターン1:詳細ページのテンプレート
// app/items/[id]/page.jsx
export default async function ItemDetailPage({ params }) {
try {
const response = await fetch(`https://api.example.com/items/${params.id}`);
if (!response.ok) {
return <div>アイテムが見つかりません</div>;
}
const item = await response.json();
return (
<div>
<h1>{item.title}</h1>
<img src={item.image} alt={item.title} />
<p>{item.description}</p>
<p>価格: {item.price}円</p>
</div>
);
} catch (error) {
return <div>エラーが発生しました</div>;
}
}
パターン2:編集ページ
// app/posts/[id]/edit/page.jsx
export default async function EditPostPage({ params }) {
const response = await fetch(`https://api.blog.com/posts/${params.id}`);
const post = await response.json();
return (
<div>
<h1>記事を編集</h1>
<form>
<input
type="text"
defaultValue={post.title}
name="title"
/>
<textarea
defaultValue={post.content}
name="content"
/>
<button type="submit">保存</button>
</form>
</div>
);
}
パターン3:関連ページへのリンク
// app/products/[id]/page.jsx
export default async function ProductPage({ params }) {
const productId = params.id;
const [product, reviews] = await Promise.all([
fetch(`https://api.shop.com/products/${productId}`).then(r => r.json()),
fetch(`https://api.shop.com/products/${productId}/reviews`).then(r => r.json())
]);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
{/* 関連ページへのリンク */}
<nav>
<a href={`/products/${productId}`}>詳細</a>
<a href={`/products/${productId}/reviews`}>レビュー</a>
<a href={`/products/${productId}/specs`}>仕様</a>
</nav>
<section>
<h2>レビュー ({reviews.length}件)</h2>
{reviews.map(review => (
<div key={review.id}>
<p>★ {review.rating}/5</p>
<p>{review.comment}</p>
</div>
))}
</section>
</div>
);
}
メタデータでもparamsを使える
ページのタイトルや説明文を動的に変えられます。
基本的な使い方
// app/products/[id]/page.jsx
// メタデータを動的に生成
export async function generateMetadata({ params }) {
const response = await fetch(`https://api.shop.com/products/${params.id}`);
const product = await response.json();
return {
title: `${product.name} - オンラインショップ`,
description: product.description
};
}
// ページコンポーネント
export default async function ProductPage({ params }) {
const response = await fetch(`https://api.shop.com/products/${params.id}`);
const product = await response.json();
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
効果:
- ブラウザのタブに商品名が表示される
- Google検索結果に適切な情報が表示される
- SNSでシェアした時にきれいに表示される
SEO対策の例
// app/articles/[slug]/page.jsx
export async function generateMetadata({ params }) {
const article = await fetch(
`https://api.blog.com/articles/${params.slug}`
).then(r => r.json());
return {
title: article.title,
description: article.excerpt,
keywords: article.tags.join(', '),
openGraph: {
title: article.title,
description: article.excerpt,
images: [article.coverImage],
type: 'article',
publishedTime: article.publishedAt,
authors: [article.author.name]
},
twitter: {
card: 'summary_large_image',
title: article.title,
description: article.excerpt,
images: [article.coverImage]
}
};
}
export default async function ArticlePage({ params }) {
const article = await fetch(
`https://api.blog.com/articles/${params.slug}`
).then(r => r.json());
return (
<article>
<h1>{article.title}</h1>
<img src={article.coverImage} alt={article.title} />
<div>{article.content}</div>
</article>
);
}
静的生成でparamsを使う
generateStaticParams を使う
ビルド時に全てのページを事前生成できます。
// app/products/[id]/page.jsx
// どのIDのページを生成するか指定
export async function generateStaticParams() {
const response = await fetch('https://api.shop.com/products');
const products = await response.json();
// [{id: '1'}, {id: '2'}, {id: '3'}, ...] を返す
return products.map(product => ({
id: product.id.toString()
}));
}
// ページコンポーネント
export default async function ProductPage({ params }) {
const response = await fetch(`https://api.shop.com/products/${params.id}`);
const product = await response.json();
return <div>{product.name}</div>;
}
メリット:
- ページが超高速(HTMLが事前に作られている)
- サーバー負荷が少ない
いつ使う?
- 商品数が決まっている
- 記事数が多くない
- 頻繁に更新されない
例:ブログの記事一覧
// app/blog/[slug]/page.jsx
export async function generateStaticParams() {
// 全記事のslugを取得
const posts = await fetch('https://api.blog.com/posts').then(r => r.json());
return posts.map(post => ({
slug: post.slug
}));
}
export default async function BlogPost({ params }) {
const post = await fetch(
`https://api.blog.com/posts/${params.slug}`
).then(r => r.json());
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
よくある質問
Q1: paramsとsearchParamsの違いは?
params: URLのパス部分から取得
/products/123
^^^
これがparams
searchParams: URLのクエリ文字列から取得
/search?q=smartphone&sort=price
^^^^^^^^^^^^^^^^^^^^^^^
これがsearchParams
使い分け:
// app/products/[id]/page.jsx
export default function ProductPage({ params, searchParams }) {
const productId = params.id; // 123
const sortBy = searchParams.sort; // "price"
const query = searchParams.q; // "smartphone"
return <div>商品 {productId}</div>;
}
Q2: paramsが取得できない時は?
チェック項目:
- フォルダ名が
[名前]
になっているか -
page.jsx
ファイルが正しい場所にあるか - 関数の引数に
{ params }
を書いているか
// ❌ これは動かない
export default function Page(params) {
return <div>{params.id}</div>;
}
// ✅ これが正しい
export default function Page({ params }) {
return <div>{params.id}</div>;
}
Q3: paramsは必須?
A: いいえ、動的ルーティングを使わないページでは不要です。
// app/about/page.jsx - paramsなし
export default function AboutPage() {
return <div>会社概要</div>;
}
// app/products/[id]/page.jsx - paramsあり
export default function ProductPage({ params }) {
return <div>商品 {params.id}</div>;
}
Q4: 日本語のURLは使える?
A: 使えますが、エンコードされます。
// app/blog/[slug]/page.jsx
export default function BlogPost({ params }) {
console.log(params.slug);
// URLが /blog/こんにちは の場合
// params.slug = "こんにちは"
return <div>{params.slug}</div>;
}
注意:
URLとしては%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF
のようにエンコードされますが、Next.jsが自動でデコードしてくれます。
よくある間違い
間違い1: paramsを直接変更しようとする
// ❌ これはエラー
export default function Page({ params }) {
params.id = "999"; // paramsは読み取り専用!
return <div>{params.id}</div>;
}
// ✅ 新しい変数に入れる
export default function Page({ params }) {
const id = params.id;
const modifiedId = id + "-modified";
return <div>{modifiedId}</div>;
}
間違い2: paramsの存在を確認しない
// ❌ paramsがない場合にエラー
export default function Page({ params }) {
return <div>{params.id.toUpperCase()}</div>;
}
// ✅ 存在確認をする
export default function Page({ params }) {
if (!params.id) {
return <div>IDが指定されていません</div>;
}
return <div>{params.id.toUpperCase()}</div>;
}
間違い3: awaitを忘れる
// ❌ paramsがPromiseになってしまう
export default function Page({ params }) {
// Next.js 15以降、paramsは非同期
return <div>{params.id}</div>; // [object Promise]と表示される
}
// ✅ asyncをつけてawaitする
export default async function Page({ params }) {
const resolvedParams = await params; // Next.js 15以降
return <div>{resolvedParams.id}</div>;
}
注意:
Next.js 15以降では、paramsが非同期になる場合があります。最新のドキュメントを確認してください。
まとめ
paramsの本質
paramsは、URLから値を取り出して使うための仕組み
- 1つのファイルで複数のページに対応できる
- URLの値に応じてデータを取得できる
- 動的なWebサイトを作るのに必須
基本の型
// app/[パラメータ名]/page.jsx
export default async function Page({ params }) {
const value = params.パラメータ名;
// この値を使ってデータ取得など
const data = await fetch(`API/${value}`);
return <div>{data.name}</div>;
}
覚えておくこと
-
フォルダ名を
[名前]
にする- この名前がparamsのキーになる
-
paramsは常に文字列
- 数値として使う時は変換が必要
-
エラー処理を忘れずに
- 不正な値が来ることも考える
-
複数のparamsも使える
- ネストしたフォルダで実現
最も大事なこと:
paramsを使えば、1つのファイルで無限のページを作れる!これがNext.jsの動的ルーティングの力です。