vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.39k stars 26.9k forks source link

Parallel / Intercepting Routes rendering 404 on page load #48289

Closed tommyboylab closed 1 year ago

tommyboylab commented 1 year ago

Verify 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: 9.4.0
      Yarn: 1.22.19
      pnpm: N/A
    Relevant packages:
      next: 13.3.1-canary.4
      eslint-config-next: 13.3.0
      react: 18.2.0
      react-dom: 18.2.0

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

No response

Link to the code that reproduces this issue

https://vercelnextjsnzcvbl-dh2v--3000--b5037fed.local-credentialless.webcontainer.io/1

To Reproduce

Describe the Bug

Upon initial client-side navigation to an intercepted route, the page is properly navigated and modal is shown. Upon reloading the page the content is not rendered, get a 404 page but params are still passed.

next-route

Expected Behavior

Navigation should first show modal on initial load from top level page. After reloading the parent page should be loaded, not have 404 but still receive props

The intention is to have a blog page with a list of articles with a set of ids (a,b,c..) which are conditionally rendered in a modal while on the blog page itself, are rendered standalone when visiting from a link / page is reloaded.

blog
├── @modal/[id]
│   └── page.tsx
├── @list
│   └── page.tsx
└── layout.tsx
├── [id]
     └── page.tsx

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Fredkiss3 commented 1 year ago

the link to the reproduction code is broken. can you update it ?

Nickman87 commented 1 year ago

@Fredkiss3 I am having similar issues so I tried to build a reproduction codebase here: https://codesandbox.io/p/sandbox/nextjs-intercepting-routes-s9j6mo

If you click open blog you go to the blog page, and there you can open a blog item, but clicking that just redirects to the actual blog page, and not just the modal. (it also renders both the blog page AND the modal, which is strange).

Not sure if this is the exact problem he is describing, because I also have an issue with a 404 locally, but cannot reproduce right now in a codesandbox.

Nickman87 commented 1 year ago

@feedthejim I think there is still something wrong with the (.) selector (https://github.com/vercel/next.js/pull/48276): If you check out this repo: https://codesandbox.io/p/sandbox/nextjs-intercepting-routes-s9j6mo You will see that if you open the blog post, there are two things happening that are off:

  1. It also loads the intercepted page als main page (it replaces the blog page with the item page)
  2. If you have the blog post modal open and refresh the page, it will give you a 404

I modified this repo to use the old (..) syntax here: https://codesandbox.io/p/sandbox/nextjs-intercepting-routes-forked-bfuitc If you try this version (only change is (.) became (..)blog and I also placed everything into a (group)), then everything works as expected.

Nickman87 commented 1 year ago

Maybe related to https://github.com/vercel/next.js/issues/48090 ?

conmute commented 1 year ago

I have some similar issue… When I have:

// ✅ Should intercept <Link> route navigation and display the my modal but do not show the `/create/note` page
src/app/@modal/(.)create/note/page.tsx

// 🔴 Should render ONLY on direct url hit, as expected. But gives 404 me instead
src/app/create/note/page.tsx

// ✅ has the <Link href="/create/note">Create note<Link>
src/app/page.tsx

// ✅ has the {children} and {modal} slots to be rendered inside
src/app/layout.tsx

Everything is working, except src/app/create/note/page.tsx gives me 404

If I will change src/app/@modal/(.)create/note/page.tsx to a different path, the src/app/create/note/page.tsx will be rendered fine but then there is no interception possible this way


More details, using the example by given link in Vercel NextJS documentation: https://github.com/vercel-labs/nextgram/tree/main/app for interceptor Modal example,

and I see that @modal/(..)photos/[id] has (..) two dots in the interceptor. But this is not working in my freshly installed app, it gives me such an error:

- error unhandledRejection: Error: Invalid interception route: /(..)create/note. Cannot use (..) marker at the root level, use (.) instead.
    at extractInterceptionRouteInformation (/Users/ross/Documents/target/Conmution/playground/fireship-nextjs/app/node_modules/next/dist/server/future/helpers/interception-routes.js:64:23)
    at generateInterceptionRoutesRewrites (/Users/ross/Documents/target/Conmution/playground/fireship-nextjs/app/node_modules/next/dist/lib/generate-interception-routes-rewrites.js:41:123)
    at Watchpack.<anonymous> (/Users/ross/Documents/target/Conmution/playground/fireship-nextjs/app/node_modules/next/dist/server/dev/next-dev-server.js:587:139)

My setup package.json has "next": "13.4.4",, with @modal/(.)create/note/page.tsx with one dot I was able to intercept but not able to render after refresh targeted URL as intended

conmute commented 1 year ago

Shortly, If I will clone https://github.com/vercel-labs/nextgram/tree/main and update npm install next@latest it will give me the same error:

> next dev

- ready started server on 0.0.0.0:3000, url: http://localhost:3000
/bin/sh: yarn: command not found
- event compiled client and server successfully in 381 ms (20 modules)
- wait compiling...
/Users/ross/Documents/target/Conmution/playground/fireship-nextjs/tmp/node_modules/next/dist/server/future/helpers/interception-routes.js:64
                throw new Error(`Invalid interception route: ${path}. Cannot use (..) marker at the root level, use (.) instead.`);
                      ^

Error: Invalid interception route: /(..)photos/[id]. Cannot use (..) marker at the root level, use (.) instead.
    at extractInterceptionRouteInformation (/Users/ross/Documents/target/Conmution/playground/fireship-nextjs/tmp/node_modules/next/dist/server/future/helpers/interception-routes.js:64:23)
    at generateInterceptionRoutesRewrites (/Users/ross/Documents/target/Conmution/playground/fireship-nextjs/tmp/node_modules/next/dist/lib/generate-interception-routes-rewrites.js:41:123)
    at Watchpack.<anonymous> (/Users/ross/Documents/target/Conmution/playground/fireship-nextjs/tmp/node_modules/next/dist/server/dev/next-dev-server.js:587:139)

Node.js v18.15.0

the update from "next": "13.3.1-canary.1" to "next": "^13.4.5" (at current moment) will break the intercepting routes, fix and update example is necessary

conmute commented 1 year ago

After some time, I did file move from /app folder outside and then inside everything worked well but with (.), here is now working example of intercepted routes that I have

https://github.com/conmute/fireship-nextjs/tree/main/app/src/app/%40modal

jessepinho commented 1 year ago

OMG — I think I've figured this out!

I was running into the same problem: the intercepted route would load fine when clicking a <Link />; but when I refreshed the page on the intercepted route, it would 404.

Here's the problem: if the layout slot isn't filled based on the current path, it will 404. You can test this by removing the slot from the layout file, and then doing a full-page load on the intercepted route path. The full-page load should work just fine now. Then, when you add the slot back to the layout file, it'll 404 again.

To fix this, you need to use a route group. Remove the slot from your root layout, and create a new layout inside your route group that just renders children and your slot:

// src/app/(home)/layout.tsx
import { ReactNode } from 'react'

export default function Layout({
  children,
  slotNameHere,
}: {
  children: ReactNode
  slotNameHere: ReactNode
}) {
  return (
    <>
      {children}
      {slotNameHere}
    </>
  )
}

Move your homepage and your intercepted routes underneath your route group:

- src
  - app
    - (home)
      - @slotNameHere
        - (.)routeName
          - page.tsx
          - ...etc

Note that, if your intercepted route is a few levels down (e.g., src/app/(home)/@slotNameHere/(.)routeName/foo/bar/page.tsx, you may need to create a page.tsx at the level right under the root of the intercepted route to prevent the same 404 issue (i.e., at src/app/(home)/@slotNameHere/(.)routeName/page.tsx). This can just be an empty component that returns null.

kachkaev commented 1 year ago

I am facing a similar bug, the details are here: https://github.com/vercel/next.js/issues/53170

@jessepinho, I am unable to fix 404s with the group. Since you are saying that you have figured it out, can it be that my reproduction example is just misconfigured? If you can spot a missing bit that is causing 404s, that’ll be awesome!

jessepinho commented 1 year ago

@kachkaev I don't have more time to look into it, but it looks like you're using three dots in the name of your (...)photo directory, which I believe is invalid.

kachkaev commented 1 year ago

@jessepinho three dots refer to root app directory so they are fine I guess. My reproduction in https://github.com/vercel/next.js/issues/53170 works the same way for (.)photo. Thanks for looking at it anyway!

jscul commented 1 year ago

Intercepting and parallel routes seem completely broken to me... or the docs are completely wrong. Either way, this is a very frustrating issue. The group solution did not work for me.

joefitter commented 1 year ago

The "trick" to this is to use default.tsx to cover non-matching slots. Adapting the example app worked for my use case

jmarbutt commented 1 year ago

I am having this issue also.

jmarbutt commented 1 year ago

I agree with @jscul on this one, so much of this seems completely broken and half baked. The docs are not correct.

AkshatKaushik3005 commented 1 year ago

has any fix been applied for this?

SomervilleTom commented 1 year ago

The "trick" to this is to use default.tsx to cover non-matching slots. Adapting the example app worked for my use case

I'm using js rather than tsx, and I have a 'default.js' (as per the documentation) in the following paths:

My hierarchy (borrowed from the "nextgram" example) is:

src/
    app/
        @modal/
            (..)photos/
                [id]/
                    page.js
            default.js
        photos/
            [id]/
                page.js
        default.js

I develop in vscode on a remote (linux) system. Here is the complaint I get in the embedded terminal of vscode when I run "yarn dev":

Error: Invalid interception route: /(..)photos/[id]. Cannot use (..) marker at the root level, use (.) instead.
    at extractInterceptionRouteInformation (<...>/node_modules/next/dist/server/future/helpers/interception-routes.js:64:23)
    at generateInterceptionRoutesRewrites (<...>/node_modules/next/dist/lib/generate-interception-routes-rewrites.js:47:121)
    at Watchpack.<anonymous> (<...>/node_modules/next/dist/server/lib/router-utils/setup-dev.js:1346:164)

So far as I can tell, this is an issue from next.js.

According to '<...>/node_modules/next/package.json', I'm running v13.5.4.

I run the most recent Javascript version of nextgram. That uses v13.3.1-canary.1 (according to its package.json), and works as documented.

It looks to me as though v13.5.4 does not align with the documentation or the next.js example.

kshyr commented 1 year ago

@SomervilleTom take a look here https://github.com/vercel-labs/nextgram/tree/main/src/app/%40modal. They use (.)photos. From Next.js docs at https://nextjs.org/docs/app/building-your-application/routing/intercepting-routes:

Note that the (..) convention is based on route segments, not the file-system.

@modal does not affect route, so they should be on one segment.

Also seems like nextgram uses v13.4.7. I just had to downgrade because I kept getting Could not find the module in the React Client Manifest. This is probably a bug in the React Server Components bundler.

SomervilleTom commented 1 year ago

@kshyr: Interesting. Please note that I wrote my version based on the most recent JAVASCRIPT (as opposed to tsx) version of the example.

I note that in this earlier version, the 'next.config.js' file contains the following 'experimental' entry:

    // ...
    experimental: {
        appDir: true,
    },
    // ...

I changed my code to use the single-level intercepting route ('src/app/@modal/(.)photos'). With that change, the example breaks with a different error complaint in the console (excess lines elided):

[Fast Refresh] rebuilding
Warning: Cannot update a component (`HotReload`) while rendering a different component (`Router`). To locate the bad setState() call inside `Router`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
    at Router (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:157:11)
    at ErrorBoundaryHandler (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:82:9)
    at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:110:11)
    at AppRouter (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:440:13)
    at ServerRoot (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/app-index.js:126:11)
    at RSCComponent
    at Root (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/app-index.js:142:11)
Uncaught TypeError TypeError: initialTree is not iterable
    at applyPatch (<...>/node_modules/next/dist/client/components/router-reducer/apply-router-state-patch-to-tree.js:15:53)
    at applyRouterStatePatchToTree (<...>/node_modules/next/dist/client/components/router-reducer/apply-router-state-patch-to-tree.js:70:30)
    at navigateReducer (<...>/node_modules/next/dist/client/components/router-reducer/reducers/navigate-reducer.js:206:83)
    at clientReducer (<...>/node_modules/next/dist/client/components/router-reducer/router-reducer.js:25:60)
    at eval (<...>/node_modules/next/dist/client/components/use-reducer-with-devtools.js:63:21)
    at updateReducerImpl (<...>/node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:11246:1)
    at updateReducer (<...>/node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:11136:1)
    at useReducer (<...>/node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:12554:1)
    at useReducer (<...>/node_modules/next/dist/compiled/react/cjs/react.development.js:1760:1)
    at useReducerWithReduxDevtoolsImpl (<...>/node_modules/next/dist/client/components/use-reducer-with-devtools.js:102:52)
    at Router (<...>/node_modules/next/dist/client/components/app-router.js:184:168)
    at renderWithHooks (<...>/node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:10730:1)
    at updateFunctionComponent (<...>/node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js:15213:1)
...

My read of this stack trace is that the Router and useReducer code in '<..>/node_modules/next/dist' is broken.

I need modal dialogs. I guess it's time to start seeking some sort of work-around. Since the "AppRouter" functionality seems to be broken, perhaps I need either somehow code modal behavior myself, revert to the "pages" paradigm, or find a way to patch this bug.

SomervilleTom commented 1 year ago

I spent the last several hours refactoring my version of nextgram to add an extra segment ('hack') to the "photos"/"@modal" portion.

I added 'console.log' statements to each 'page.js', to each 'default.js', and to each 'layout.js' in hopes working out what elements are actually rendered.

The result is that, as far as I can tell, the intercepting route is simply ignored.

Here is my new structure:

src/
    app/
        hack/
            @modal/
                (..)photos/
                    [id]/
                        page.js
                default.js
            photos/
                [id]/
                    page.js
            default.js
        layout.js
        page.js

The top-level 'page.js' ('src/app/page.js') contains a Link button whose href is '/hack/photos'. This opens the "nextgram' page as expected. Each item in that page is a Link whose href is 'hack/photos/${id}'. In the original nextgram example, each of those causes the intercepting route to be invoked when the item is clicked.

In this version, when one of those is clicked, the plain (non-modal) photos/[id] page is rendered.

At no time is there any evidence that any '@model' descendent is ever rendered.

It appears to me that in the v13.5.4 release, parallel/intercepting routes are either broken or do nothing.

SomervilleTom commented 1 year ago

I spent the rest of the afternoon trying to patch v13.4.7 (the version used in the most recent nextgram example) to use the "https" (rather than "http") protocol. The next.js code successfully resisted this attempt.

My next step is to punt the "AppRoute" approach altogether, at least for now, and revert to the less-favored "Pages" approach. I don't need to do any streaming, so perhaps this will be more fruitful.

At least for now, the APIRoute approach is a dead-end for me.

SomervilleTom commented 1 year ago

I was finally able to get a copy of the nextgram example working on my system. It worked after I did the following (not all might be needed)

  1. Add sections to 'package.json'

    The following need to be added to "dependencies":

    "@headlessui/react": "^1.7.15",
    "clsx": "^1.2.1",

    A "devDependencies" apparently needs to be present:

    "devDependencies: {
        "@types/node": "20.3.1",
        "@types/react": "^18.2.14",
        "autoprefixer": "^10.4.14",
        "postcss": "^8.4.24",
        "sass": "^1.63.6",
        "tailwindcss": "^3.3.2",
        "typescript": "^5.1.3"
    },
  2. Add top-level config files

    • 'tsconfig.json'

    • 'postcss.config.js'

    • 'next.config.js'

      This includes the following "experimental" section:

      experimental: {
        appDir: true,
      },

      This is specific to 'v13.4.7' of 'next.js'

    • 'tailwind.config.js'

  3. Use explicit paths in 'app/@modal/(.)photos/[id]/page.js'

    React encourages the use of a top-level 'jsconfig.json' file that provides more convenient expressions in import statements. It appears that the '@whatever' syntax breaks that facility.

    I therefore had to change 'app/@modal/(.)photos/[id]/page.js' as follows:

    Failing:

    import Frame from '@/components/frame/Frame';
    import Modal from "@/components/modal/Modal";

    Successful:

    import Frame from '../../../../components/frame/Frame'
    import Modal from "../../../../components/modal/Modal";

    Please note that I am using Javascript ('.js') rather than Typescript ('.ts').

Now that I have the nextgram example working with v13.4.7, I'll update to v13.5.4 and see if I can make it work.

If that succeeds, then I'll try adding back the MaterialUI code (MaterialUI is the point of the exercise for me).

I note the dependency on '@headlessui/react' -- that may explain a lot.

SomervilleTom commented 1 year ago

I updated to v13.5.4 and all is still well (running the 'nextgram' example).

I removed the "experimental" section from 'next.config.js'.

Now I'll try adding back in the MUI behavior (piece by piece).

joefitter commented 1 year ago

Hey, pretty sure this bug has been fixed but not included in a major release yet. I updated to 13.5.4-canary.6 which fixed the MUI issues

SomervilleTom commented 1 year ago

I was finally able to apply a patch to "start-server.js" for v13.5.4 so that 'yarn dev' starts a dev server listening on https instead of http, using the nextgram example code. This is needed because the rest of my environment needs the front end running https instead of http, and because it needs to use the same certs as the rest of the platform (rather than any special-snowflake self-signed certs).

No console complaints, and the MUI version of 'nextgram' behaves as expected.

I'm finally able to resume development on my own MUI modal behavior.

NOTE: I'm pretty sure that adding "@headlessui/react": "^1.7.15" to 'package.json' was a crucial step missing from my first go-round.

feedthejim commented 1 year ago

I'm gonna close this issue because it seems while it's clear people are having various issues with parallel routes, it's unclear to me if they are not related to the original issue in particular.

Please open new issues with repros and a description of what doesn't work, and feel free to ping me directly there, I promise to take a look.

github-actions[bot] commented 1 year ago

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.