nextjs-pathname-id-fetch
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNext.js: Pathname ID Fetch Pattern
Next.js:路径名ID获取模式
When This Pattern Applies
适用场景
Use this pattern whenever a page needs to load data based on whatever identifier appears in the URL. Common scenarios include:
- Detail pages for products, posts, or users (,
/products/{id})/blog/{slug} - Admin dashboards that drill into a selected resource ()
/admin/orders/{orderId} - Documentation or knowledge bases with nested paths ()
/docs/getting-started/installation
If the requirement says the data should change depending on the current URL path, the route segment must be dynamic (e.g., , , ).
[id][slug][...slug]当页面需要根据URL中的标识符加载数据时,即可使用此模式。常见场景包括:
- 产品、文章或用户的详情页(、
/products/{id})/blog/{slug} - 可深入查看选定资源的管理控制台()
/admin/orders/{orderId} - 带有嵌套路径的文档或知识库()
/docs/getting-started/installation
如果需求要求数据根据当前URL路径变化,那么路由段必须是动态的(例如、、)。
[id][slug][...slug]The Pattern
模式说明
✅ Recommended implementation
1. Create a dynamic folder: app/[id]/page.tsx
2. Access the parameter: const { id } = await params;
3. Fetch data using that identifier
4. Render the requested information❌ Pitfall
Using app/page.tsx for this scenario prevents access to per-path identifiers.✅ 推荐实现方式
1. 创建动态文件夹:app/[id]/page.tsx
2. 获取参数:const { id } = await params;
3. 使用该标识符获取数据
4. 渲染请求的信息❌ 常见误区
在此场景下使用app/page.tsx将无法获取每个路径对应的标识符。Complete Implementation Example
完整实现示例
typescript
// app/[id]/page.tsx
// IMPORTANT: Server component (NO 'use client' needed!)
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
// Next.js 15+: params must be awaited
const { id } = await params;
// Fetch data using the ID from the URL
const response = await fetch(`https://api.example.com/products/${id}`);
const product = await response.json();
// Return JSX with the fetched data
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
</div>
);
}typescript
// app/[id]/page.tsx
// IMPORTANT: Server component (NO 'use client' needed!)
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
// Next.js 15+: params must be awaited
const { id } = await params;
// Fetch data using the ID from the URL
const response = await fetch(`https://api.example.com/products/${id}`);
const product = await response.json();
// Return JSX with the fetched data
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
</div>
);
}File Structure
文件结构
app/
└── [id]/ ← Dynamic route folder with brackets
└── page.tsx ← Server component pageURL Mapping:
- → params =
/123{ id: '123' } - → params =
/abc{ id: 'abc' } - → params =
/product-xyz{ id: 'product-xyz' }
app/
└── [id]/ ← Dynamic route folder with brackets
└── page.tsx ← Server component pageURL映射:
- → params =
/123{ id: '123' } - → params =
/abc{ id: 'abc' } - → params =
/product-xyz{ id: 'product-xyz' }
Key Rules
核心规则
1. Folder Name MUST Use Brackets
1. 文件夹名称必须使用方括号
✅ app/[id]/page.tsx
✅ app/[productId]/page.tsx
✅ app/[slug]/page.tsx
❌ app/id/page.tsx (no brackets = static route)
❌ app/page.tsx (can't access params here)✅ app/[id]/page.tsx
✅ app/[productId]/page.tsx
✅ app/[slug]/page.tsx
❌ app/id/page.tsx (no brackets = static route)
❌ app/page.tsx (can't access params here)2. This is a Server Component (Default)
2. 默认使用Server Component
typescript
// ✅ CORRECT - No 'use client' needed
export default async function Page({ params }) {
const { id } = await params;
const data = await fetch(`/api/${id}`);
return <div>{data.name}</div>;
}
// ❌ WRONG - Don't add 'use client' for server components
'use client'; // ← Remove this!
export default async function Page({ params }) { ... }typescript
// ✅ CORRECT - No 'use client' needed
export default async function Page({ params }) {
const { id } = await params;
const data = await fetch(`/api/${id}`);
return <div>{data.name}</div>;
}
// ❌ WRONG - Don't add 'use client' for server components
'use client'; // ← Remove this!
export default async function Page({ params }) { ... }3. Params Must Be Awaited (Next.js 15+)
3. Next.js 15+中必须等待Params
typescript
// ✅ CORRECT - Next.js 15+
export default async function Page({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params; // Must await
// ...
}
// ⚠️ OLD (Next.js 14 and earlier - deprecated)
export default async function Page({
params,
}: {
params: { id: string };
}) {
const { id } = params; // No await needed in old versions
// ...
}typescript
// ✅ CORRECT - Next.js 15+
export default async function Page({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params; // Must await
// ...
}
// ⚠️ OLD (Next.js 14 and earlier - deprecated)
export default async function Page({
params,
}: {
params: { id: string };
}) {
const { id } = params; // No await needed in old versions
// ...
}4. Keep It Simple - Don't Over-Nest
4. 保持简洁 - 不要过度嵌套
✅ app/[id]/page.tsx (simple, clean)
❌ app/products/[id]/page.tsx (only if explicitly required!)Unless requirements explicitly call for , keep the structure at the top level ().
/products/[id]app/[id]/page.tsx✅ app/[id]/page.tsx (simple, clean)
❌ app/products/[id]/page.tsx (only if explicitly required!)除非需求明确要求使用,否则请将结构保持在顶层()。
/products/[id]app/[id]/page.tsxTypeScript: NEVER Use any
Type
anyTypeScript:绝不要使用any
类型
anyThis codebase has enabled. Using will cause build failures.
@typescript-eslint/no-explicit-anyanytypescript
// ❌ WRONG
function processProduct(product: any) { ... }
// ✅ CORRECT - Define proper types
interface Product {
id: string;
name: string;
price: number;
}
function processProduct(product: Product) { ... }
// ✅ ALSO CORRECT - Use unknown if type truly unknown
function processData(data: unknown) {
// Type guard required before using
if (typeof data === 'object' && data !== null) {
// ...
}
}此代码库已启用。使用会导致构建失败。
@typescript-eslint/no-explicit-anyanytypescript
// ❌ WRONG
function processProduct(product: any) { ... }
// ✅ CORRECT - Define proper types
interface Product {
id: string;
name: string;
price: number;
}
function processProduct(product: Product) { ... }
// ✅ ALSO CORRECT - Use unknown if type truly unknown
function processData(data: unknown) {
// Type guard required before using
if (typeof data === 'object' && data !== null) {
// ...
}
}Common Variations
常见变体
Different Parameter Names
不同的参数名称
typescript
// app/[productId]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ productId: string }>;
}) {
const { productId } = await params;
// ...
}
// app/[slug]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
// ...
}typescript
// app/[productId]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ productId: string }>;
}) {
const { productId } = await params;
// ...
}
// app/[slug]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
// ...
}Multiple Parameters
多个参数
typescript
// app/[category]/[id]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ category: string; id: string }>;
}) {
const { category, id } = await params;
const data = await fetch(`/api/${category}/${id}`);
// ...
}typescript
// app/[category]/[id]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ category: string; id: string }>;
}) {
const { category, id } = await params;
const data = await fetch(`/api/${category}/${id}`);
// ...
}Complete Working Example
完整可用示例
typescript
// app/[id]/page.tsx - Product detail page
interface Product {
id: string;
name: string;
description: string;
price: number;
inStock: boolean;
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
// Get the ID from the URL
const { id } = await params;
// Fetch product data using the ID
const response = await fetch(
`https://api.example.com/products/${id}`,
{ cache: 'no-store' } // Always fresh data
);
if (!response.ok) {
throw new Error('Failed to fetch product');
}
const product: Product = await response.json();
// Render the product
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<div>
<strong>Price:</strong> ${product.price}
</div>
<div>
<strong>Availability:</strong>{' '}
{product.inStock ? 'In Stock' : 'Out of Stock'}
</div>
</div>
);
}typescript
// app/[id]/page.tsx - Product detail page
interface Product {
id: string;
name: string;
description: string;
price: number;
inStock: boolean;
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
// Get the ID from the URL
const { id } = await params;
// Fetch product data using the ID
const response = await fetch(
`https://api.example.com/products/${id}`,
{ cache: 'no-store' } // Always fresh data
);
if (!response.ok) {
throw new Error('Failed to fetch product');
}
const product: Product = await response.json();
// Render the product
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<div>
<strong>Price:</strong> ${product.price}
</div>
<div>
<strong>Availability:</strong>{' '}
{product.inStock ? 'In Stock' : 'Out of Stock'}
</div>
</div>
);
}Quick Checklist
快速检查清单
Before shipping a pathname-driven detail page, confirm:
- The route folder uses brackets (e.g., )
app/[id]/page.tsx - The component stays server-side (no needed)
'use client' - The prop is typed as
paramsfor Next.js 15+Promise<{ id: string }> - You await the params and read the identifier safely
- Data fetching logic uses that identifier
- Rendering handles loading/error states appropriately
- Types are explicit—never fall back to
any
在发布基于路径名的详情页之前,请确认:
- 路由文件夹使用方括号(例如)
app/[id]/page.tsx - 组件保持在服务端(无需添加)
'use client' - 对于Next.js 15+,属性的类型为
paramsPromise<{ id: string }> - 已等待params并安全读取标识符
- 数据获取逻辑使用了该标识符
- 渲染逻辑适当处理了加载/错误状态
- 类型明确——绝不使用
any
When to Use the Comprehensive Skill Instead
何时使用完整技能
This micro-skill covers the simple "pathname ID fetch" pattern. Use the comprehensive skill for:
nextjs-dynamic-routes-params- Catch-all routes ()
[...slug] - Optional catch-all routes ()
[[...slug]] - Complex multi-parameter routing
- Advanced routing architectures
- Detailed routing decisions
For the simple case of "fetch data by ID from URL", this skill is all you need.
此微技能涵盖了简单的“路径名ID获取”模式。在以下场景中,请使用完整的技能:
nextjs-dynamic-routes-params- 捕获所有路由()
[...slug] - 可选捕获所有路由()
[[...slug]] - 复杂的多参数路由
- 高级路由架构
- 详细的路由决策
对于“通过URL中的ID获取数据”的简单场景,本技能已足够。