You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13 App Router中带use client的动态路由静态导出失败

问题:Next.js 13 App Router动态路由静态导出预渲染错误

本地运行动态路由页面(如localhost:3000/tickets/2)正常,但执行npm run build导出静态站点时出现以下错误:

Error occurred prerendering page "/tickets/[id]". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: Cannot read properties of undefined (reading 'id')

已知条件:

  • 页面顶部添加了"use client",因为需要使用useState钩子;
  • 移除"use client"和状态管理代码后,npm run build可顺利导出静态页面(Next.js会为每个ticket id生成如/tickets/1、/tickets/2的静态页面)。

相关配置代码:
next.config.js

module.exports = {
  output: "export",
  images: {
    unoptimized: true,
  }
};

app/tickets/[id]/page.js

"use client";

import { useState } from "react";
import Select from "components/Select";

const TICKETS = [
  { id: 0, status: "open" },
  { id: 1, status: "open" },
  { id: 2, status: "resolved" },
  { id: 3, status: "canceled" },
  { id: 4, status: "resolved" }
]

export async function generateStaticParams() {
  return TICKETS.map((ticket) => ({
    id: `${ticket.id}`,
  }));
}

export default function Ticket({ params }) {
  const { id } = params;
  const ticket = TICKETS[id];
  const [status, setStatus] = useState(ticket.status);

  return (
    <div>
      <h1>{ticket.id}</h1>
      <Select
        onValueChange={(e) => {
          setStatus(e);
        }}
        value={status}
      />
    </div>
  );
}

原因分析

generateStaticParams是Next.js的服务器端专属函数,用于静态导出时生成路由参数。当页面添加"use client"指令后,整个组件会被标记为客户端组件,服务器端不会执行客户端组件中的generateStaticParams函数,导致预渲染时无法获取params,进而出现Cannot read properties of undefined (reading 'id')错误。

本地开发环境下,Next.js会在服务器端兜底处理路由参数传递,所以不会暴露这个问题;但静态导出时,预渲染逻辑严格依赖服务器端的参数生成逻辑,客户端组件中的generateStaticParams完全失效。


解决方案

将服务器端逻辑(参数生成、数据获取)与客户端逻辑(状态管理、交互)拆分,让generateStaticParams保留在服务器组件中:

步骤1:拆分服务器组件(page.js)

修改app/tickets/[id]/page.js,移除"use client",仅保留参数生成和数据传递逻辑:

import TicketClient from "./TicketClient";

const TICKETS = [
  { id: 0, status: "open" },
  { id: 1, status: "open" },
  { id: 2, status: "resolved" },
  { id: 3, status: "canceled" },
  { id: 4, status: "resolved" }
];

export async function generateStaticParams() {
  return TICKETS.map((ticket) => ({
    id: `${ticket.id}`,
  }));
}

export default function Ticket({ params }) {
  const { id } = params;
  const ticket = TICKETS[id];
  // 将获取到的ticket数据传递给客户端组件
  return <TicketClient ticket={ticket} />;
}

步骤2:创建客户端组件(TicketClient.js)

在同目录下创建TicketClient.js,添加"use client"和状态管理逻辑:

"use client";

import { useState } from "react";
import Select from "components/Select";

export default function TicketClient({ ticket }) {
  const [status, setStatus] = useState(ticket.status);

  return (
    <div>
      <h1>{ticket.id}</h1>
      <Select
        onValueChange={(e) => {
          setStatus(e);
        }}
        value={status}
      />
    </div>
  );
}

验证

执行npm run build,静态导出将正常生成所有tickets/[id]对应的静态页面,同时客户端组件的状态管理逻辑也能正常工作。


内容的提问来源于stack exchange,提问作者sea_monster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:10:04