
next.js app router 中 not-found.js 未渲染 404 页面,通常源于 node.js 版本不满足最低要求(v16.14+),或 notfound() 调用位置/时机不当;升级 node.js 并确保在服务端逻辑中尽早触发 notfound() 是关键。
next.js app router 中 not-found.js 未渲染 404 页面,通常源于 node.js 版本不满足最低要求(v16.14+),或 notfound() 调用位置/时机不当;升级 node.js 并确保在服务端逻辑中尽早触发 notfound() 是关键。
在 Next.js(v13.4+)的 App Router 模式下,not-found.js 是专用于自定义 404 页面的特殊文件,需放置在 app/ 目录下(如 app/not-found.js),且必须导出默认 React 组件。但该文件不会自动触发——它仅作为“兜底 UI”,真正触发跳转至该页面的是 next/navigation 中的 notFound() 函数。若 notFound() 未被正确调用,或调用时环境/时机不合规,就会出现空白页而非预期的 404 提示。
✅ 正确使用 notFound() 的前提条件
-
Node.js 版本必须 ≥ v16.14
Next.js 官方明确要求 Node.js 最低版本为 v16.14.0(见 v13.4 文档)。低于此版本(例如 v16.13.2)会导致 notFound() 在服务端调用后静默失效,页面渲染为空白,且无任何警告或错误日志——这是极易被忽略的关键陷阱。
✅ 解决方案:升级 Node.js 至稳定 LTS 版本(推荐 v18.16.1+ 或 v20.x):# 使用 nvm 升级示例 nvm install 18.16.1 nvm use 18.16.1 node -v # 确认输出 v18.16.1
notFound() 必须在 Server Component 中同步/异步调用,且不可延迟到客户端
notFound() 是服务端导航指令,仅在 Server Component(即 async 页面组件或服务端函数中)有效。它不能在 useEffect、事件处理器或 Client Component 中调用。调用必须发生在数据获取失败的明确分支中,且早于任何 JSX 渲染
如你提供的代码所示,应在 getTicket() 内部 !res.ok 时立即调用 notFound(),并确保该函数是 async 的、被页面组件 await 执行——这能保证 Next.js 在渲染前就中断流程并跳转至 not-found.js。
✅ 推荐的健壮实现方式(含错误防护)
// app/tickets/[id]/page.tsx
import { notFound } from 'next/navigation';
async function getTicket(id: string) {
try {
const res = await fetch(`http://localhost:4000/tickets/${id}`, {
next: { revalidate: 60 },
// 建议添加超时和错误处理
cache: 'no-store',
});
if (!res.ok) {
console.warn(`Failed to fetch ticket ${id}: ${res.status} ${res.statusText}`);
notFound(); // ✅ 此处触发 404 路由
}
return await res.json();
} catch (err) {
console.error('Network or parsing error:', err);
notFound(); // ✅ 网络异常也应导向 404
}
}
export default async function TicketDetails({ params }: { params: { id: string } }) {
const ticket = await getTicket(params.id);
return (
<main classname="container mx-auto p-4"><nav><h2>Ticket Details</h2>
</nav><div classname="card bg-white rounded-lg shadow p-6">
<h3 classname="text-xl font-bold">{ticket.title}</h3>
<small classname="text-gray-500">Created by {ticket.user_email}</small>
<p classname="mt-2">{ticket.body}</p>
<div classname="{`mt-4" inline-block px-3 py-1 text-sm rounded-full ticket.priority="==" text-red-800 : text-yellow-800 text-green-800>
{ticket.priority} priority
</div>
</div>
</main>
);
}
同时,确保 app/not-found.tsx 存在且导出有效组件:
// app/not-found.tsx
export default function NotFound() {
return (
<div classname="flex flex-col items-center justify-center min-h-screen bg-gray-50 p-4">
<h2 classname="text-2xl font-bold text-gray-800">Page Not Found</h2>
<p classname="text-gray-600 mt-2">The ticket you requested does not exist.</p>
<a href="/" classname="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition">
Return Home
</a>
</div>
);
}
⚠️ 注意事项与调试建议
- ❌ 不要在 useEffect 或事件中调用 notFound():它仅在服务端有效,客户端调用会抛出 Invariant: notFound() can only be used in Server Components 错误。
- ❌ 避免在 layout.tsx 或 loading.tsx 中调用:这些文件不支持导航中断,notFound() 将被忽略。
- ✅ 验证是否生效:启动开发服务器后,手动访问一个不存在的路由(如 /tickets/999999),观察浏览器地址栏是否保持原路径(说明未跳转),并检查控制台/终端有无 notFound() 调用日志——若无反应,优先检查 Node.js 版本。
- ✅ 启用严格模式排查:在 next.config.js 中添加 experimental: { missingSuspenseWithCSRB: true } 可帮助捕获潜在的服务端渲染问题(Next.js 13.4+)。
总结:not-found.js 是 Next.js App Router 强大而简洁的 404 解决方案,但其可靠性高度依赖底层运行时兼容性与调用规范。升级 Node.js 至 v16.14+ 是前提,将 notFound() 置于服务端数据获取失败的第一时间点是核心实践。遵循上述结构与校验步骤,即可稳定启用自定义 404 页面。











