next.js: [NEXT-1030] `output: 'export'` with `use client` in dynamic routes doesn't work

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.4.0: Mon Mar  6 20:59:28 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T6000
    Binaries:
      Node: 18.13.0
      npm: 8.19.3
      Yarn: 1.22.19
      pnpm: N/A
    Relevant packages:
      next: 13.2.5-canary.32
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true), Static HTML Export (output: “export”)

Link to the code that reproduces this issue

https://github.com/qpre/next-app-dir-dynamic-with-static-export

To Reproduce

  • run yarn install
  • run yarn build
  • open the out folder and see that static pages are rendered but /toto/[toto].html is missing, based on the docs it should be exported.

Describe the Bug

static routes are exported but the ones with dynamic params are not.

Expected Behavior

based on the docs dynamic routes using ‘use client’ should be exported.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-1030

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 130
  • Comments: 59 (11 by maintainers)

Commits related to this issue

Most upvoted comments

We hear you! This is not something we are working on at the moment.

To learn more, see my previous comment. Also read about our current focus in the June 2023 blog post.

Remember to upvote the original post at the top by clicking the “👍” button so we have visibility when sorting.

“next”: “^13.4.4”,

Still not found dynamic routes in out directory after build.

Will we have this feature or will not support it?

Servers other than deploy only support export. Therefore, the product cannot be deployed.

Do we have any solution?

This seems like basic functionality for a web app and I think it should be given a higher priority.

Consider the Reddit website. Routing includes:

  • /
  • /r/:subredditName
  • /r/:subredditName/new
  • /r/:subredditName/top
  • /r/:subredditName/top/?t=all
  • /r/:subredditName/comments/:postId/:postName/

If the Reddit site was built as a Next.js static app, using any of the dynamic segments here wouldn’t work - and it’s not like Reddit can use generateStaticParams and rebuild their site for each new subreddit/post/comment that gets created.

This isn’t a problem specific to apps as big as Reddit, either. Any small app that contains dynamic content could want routing like this. And the reality of it is that some (most?) web apps will start out not needing the server-side features that Next.js provides and will just function as a static site or SPA. Sure, these server-side features can be helpful and very powerful, but I for one was drawn to Next.js purely for the developer experience and routing features.

I don’t want to be forced into using any server-side features when I don’t need them yet. Static sites or SPAs are always going to be cheaper to host and easier to manage at a low scale than a web app requiring a server.

I love the framework! Really want to use it, but this is a big blocker for me (and probably others). I think this should also be called out more prominently in the docs - building an entire app in Next.js then deploying it, only to find that dynamic routing isn’t supported for static apps will turn many people off from the framework.

I discussed this problem with the team, and we decided it extends beyond output: 'export'.

Previously, with Pages Router, the routes manifest was downloaded by the client. One of the improvements with App Router is that this is no longer necessary, meaning fewer bytes need to download.

What we need is some way to indicate “offline” to prefetch routes, bundles, etc. Then we can use this new mode with output: 'export'.

The best you can do today with output: 'export' is generate the routes at build time using generateStaticParams() like the following:

https://github.com/qpre/next-app-dir-dynamic-with-static-export/pull/1

We’ll follow up here once we have a proper solution, thanks!

I think this is a basic and very important feature for creating a static site and should be mentioned in the documentation.

It would be nice to get some commentary on this issue. Next is now the official way to build React apps yet this quite basic functionality doesn’t function. Until this is resolved I think either the NextJS docs should be updated OR React should be updated to reflect that this is not currently a viable option.

We should update the docs here https://nextjs.org/docs/app/building-your-application/deploying/static-exports#dynamic-fetching-with-client-components and mention this feature is not supported at all.

This seems like basic functionality for a web app and I think it should be given a higher priority.

Consider the Reddit website. Routing includes:

  • /

  • /r/:subredditName

  • /r/:subredditName/new

  • /r/:subredditName/top

  • /r/:subredditName/top/?t=all

  • /r/:subredditName/comments/:postId/:postName/

If the Reddit site was built as a Next.js static app, using any of the dynamic segments here wouldn’t work - and it’s not like Reddit can use generateStaticParams and rebuild their site for each new subreddit/post/comment that gets created.

This isn’t a problem specific to apps as big as Reddit, either. Any small app that contains dynamic content could want routing like this. And the reality of it is that some (most?) web apps will start out not needing the server-side features that Next.js provides and will just function as a static site or SPA. Sure, these server-side features can be helpful and very powerful, but I for one was drawn to Next.js purely for the developer experience and routing features.

I don’t want to be forced into using any server-side features when I don’t need them yet. Static sites or SPAs are always going to be cheaper to host and easier to manage at a low scale than a web app requiring a server.

I love the framework! Really want to use it, but this is a big blocker for me (and probably others). I think this should also be called out more prominently in the docs - building an entire app in Next.js then deploying it, only to find that dynamic routing isn’t supported for static apps will turn many people off from the framework.

@ides15 This is so true! If we can’t fix it at the moment, please at least make it as clear as possible in the docs, so that more people will not fall into this trap…

still have a problem in 13.4.5

It doesn’t work with 13.4.2-canary.3 version as well. Facing the same issue , all dynamic routes don’t work and it adds .txt extension to each of them. Trying to deploy on Netlify.

still have a problem in 13.4.7

OK this has clearly been misinterpreted. If I came across as either passive or agressive that was not my intent. I think my comment was more toward the React team than Next - eg if something is not ready it should be put in the official docs.

To be clear, I have absolutely no issue with Next having these issues, it is to be expected. However that is not in keeping with it being the first recommended way of starting a React project when, combined with the App Router now being stable & the preferred way of starting a Next project. This issue addresses a very basic React use case.

This is probably not the best place for further discussion. If I’ve offended anybody with my last comment then either reach me on Twitter (the_chrishurst) or in this discussion that I started.

Hi, we are having the same problem, it treats dynamic route as it does not exist and redirects to root page.

Temporary fix (but ugly one) is to use useSearchParams and instead of item/123 to use item?id=123

I confirmed this is still a bug. Feel free to create a PR with a test. Otherwise I will come back to this in a couple weeks 👍

Thanks for all the feedback!

  • 📘 The docs for output: export have been updated to describe the Unsupported Features.
  • 📕 Both next dev & next build will error when an unsupported feature is detected (try npm install next@canary)
  • 📙 A related bug (but not strictly output export) where use client + generateStaticParams() also errors properly
  • 📗 The remaining work is described in this new issue: #54393

I’m going to close this issue since it has been a catch-all for several issues and most have been resolved.

Please add a 👍 reaction to #54393 if this describes the problem you’re having (source).

If your problem is different, please create a new issue with the steps to reproduce using canary, thanks!

Encountered the same problem and came up with a workaround (**edit: only works for static slugs as @durchanek mentioned).

Problem

/* /app/test/[slug]/page.jsx */
'use client'
import { useState } from 'react'

const ClientPage = ({ params }: { params: { slug: string } }) => {
  const [value, setValue] = useState(params.slug)
  return (
    <div>
      <p>foo is [{ value }]</p>
      <button type="button" onClick={ () => setValue("foo") }>Set foo</button>
    </div>
  )
}

export function generateStaticParams() {
  return [{ slug: 'hello' }, { slug: 'world' }]
}

export default ClientPage

build result

Route (app)
┌ ○ /
└ λ  /test/[slug] 

λ  (Server)  server-side renders at runtime

Workaround

Move 'use client' and client-side code into component:

/* /app/test/[slug]/page.jsx */
import Foo from './Foo'

const ServerPage = ({ params }: { params: { slug: string } }) => {
  return (
    <Foo slug={ params.slug } />
  )
}
export function generateStaticParams() {
  return [{ slug: 'hello' }, { slug: 'world' }]
}
export default ServerPage
/* /app/test/[slug]/Foo.jsx */
'use client'
import { useState } from 'react'

const Foo = ({ slug }) => {
  const [value, setValue] = useState(slug)

  return (
    <div>
      <p>foo is [{ value }]</p>
      <button type="button" onClick={ () => setValue("foo") }>Set foo</button>
    </div>
  )
}
export default Foo

build result

Route (app)
┌ ○ /
├ ● /test/[slug]
├   ├ /test/hello
└   └ /test/world

●  (SSG)     automatically generated as static HTML + JSON

Using Next.js v13.4.12

Confirmed that it is failing on 13.4.0 for me too. Looks like there is an issue opened for this new error: https://github.com/vercel/next.js/issues/49059

@durchanek indeed…hopefully it would be helpful for someone while we wait for a real fix.

@mingyeungs Unfortunately, this is not a full workaround because it requires slugs to be static.

This still fails as of 13.4.0. At least there is an error now.

> Build error occurred Error: invariant: Undefined export worker for app dir, this is a bug in Next.js.

Hi, I am also running into this issue here. Thanks for looking into it, @styfle!

I created a sample app to demonstrate the workaround suggested by @kneza23 (using query parameters):

https://github.com/nareshbhatia/nextjs-nested-layouts

I have a couple of questions:

  1. When the query parameters change, is it considered a new page and is the entire page re-rendered? The console.logs in my app seem to suggest that.
  2. When the page is re-rendered, I don’t see an API call to fetch the list on the LHS. I only see the call to fetch the detail on the RHS. This is confusing. If the entire page is re-rendered I was expecting to see both API calls.
screenshot

this bug is so important. and no priority is given. This bug means that there is no compatibility with static sites. and in the doc it says yes, but this bug proves no. routing is basic in every application

Hi, we are having the same problem, it treats dynamic route as it does not exist and redirects to root page.

Temporary fix (but ugly one) is to use useSearchParams and instead of item/123 to use item?id=123

This IMO is the best “workaround”. Everyone can still understand this model, and does not require infra changes

I probably found a tricky workaround for this. Basically in the Server Component page.js inside the /app/blog/[slug] folder I added the function generateStaticParams that returns just an array like this: [{ slug: ‘__slug__’ }] and in the render of the page I call a Client Component <BlogSinglePost />. In this way when I build the app with static export I have the file __slug__.html inside the folder /out/blog. Then in nginx I use a location rewrite directive for point to that file.

Notice that for access the url params inside the client component you can’t use the hook useParams but you have to use window.location.

I know that this way is not a fix, is just a temporarily workaround for use the dynamic route in static export of NextJs with the app folder. I hope that this thing could be useful for someone.

PS: I have tested but not that much to say that is a safe and compliant way for handle this situation.

@hopedope Thanks, I must have missed that link in the original post 👍

I’ll talk to the docs team to remove that section and figure out why it was documented in the first place (because we obviously shouldn’t document features that aren’t implemented 😅)

Can confirm. I’m still trying to find a version in which this actually works.

The .js files for the dynamic pages are actually in the out/_next/static/chunks/app folder but the root index just doesn’t do any routing to them when the page is requested.