sea_monster
sea_monster

Reputation: 751

In Next.js 13 using App Router, why can't I export dynamic routes with "use client"?

I'm using Next.js 13 with the App Router and have a page that uses dynamic routes. It works well when I run the server locally (localhost:3000//tickets/2, for instance), but fails with prerender errors when I try to export a static site via 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')

Some notes:

I'm not sure why I'm unable to export a static site with useState on a page accessed via a dynamic route. Here's my setup, with just the relevant parts:

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" }
]

// Dynamic routes; create a page for each ticket ID
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>
  );
}

Upvotes: 9

Views: 8717

Answers (1)

Seonghyeon Cho
Seonghyeon Cho

Reputation: 432

Try to move client component out of page.js and import it instead.

generateStaticParams is not supported in client component, but importing client component in server component is ok.

Ticket.js

"use client"

// ..imports

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>
  );
}

Page.js

import Ticket from "./ticket";

// ...

// Dynamic routes; create a page for each ticket ID
export async function generateStaticParams() {
  return TICKETS.map((ticket) => ({
    id: `${ticket.id}`,
  }));
}

export default function Page({ params }) {
  return <Ticket id={params.id}>;
}

Reference

Upvotes: 14

Related Questions