Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intercepting parallel routes including dynamic route not working within the same directory #52533

Closed
1 task done
zoogeny opened this issue Jul 11, 2023 · 25 comments · Fixed by #58198
Closed
1 task done
Labels
bug Issue was opened via the bug report template. locked

Comments

@zoogeny
Copy link

zoogeny commented Jul 11, 2023

Verify canary release

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

Provide environment information

Operating System:
      Platform: darwin
      Arch: x64
      Version: Darwin Kernel Version 22.5.0: Thu Jun  8 22:22:22 PDT 2023; root:xnu-8796.121.3~7/RELEASE_X86_64
    Binaries:
      Node: 18.16.1
      npm: 9.8.0
      Yarn: 1.22.19
      pnpm: N/A
    Relevant Packages:
      next: 13.4.10-canary.3
      eslint-config-next: 13.4.9
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.1.6
    Next.js Config:
      output: N/A

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

App Router

Link to the code that reproduces this issue or a replay of the bug

https://github.com/zoogeny/next-js-interceptor-parallel-dynamic

To Reproduce

Checkout, install the project and run the dev server

git checkout https://github.com/zoogeny/next-js-interceptor-parallel-dynamic.git   
cd next-js-interceptor-parallel-dynamic
npm install
npm run dev

Load a page in the blog (e.g. http://localhost:3000/blog/first-post) and you will see a basic interface:

params.slug: first-post
Same dir
Nested dir

If you click the Nested dir link you will correctly get a parallel slot rendered below the links with a red border. If you click within the border the slot will be removed.

If you click the Same dir link you will navigate the entire page. This is not expected - it should be intercepted and render inside the parallel slot.

Describe the Bug

It appears that interception of routes within the same directory does not work. e.g. I have a directory structure as follows:

app
  /blog
    /[slug]
      page.tsx
    /@modal
      /(.)[slug]
        page.tsx
      /(.)summary
        /[slug]
          page.tsx
      default.tsx
    layout.tsx
    page.tsx

While I am on the blog/some-page page I can intercept <Link> to blog/summary/another-page but I cannot intercept <Link> to blog/another-page.

Expected Behavior

When on a page blog/some-page I would expect to be able to intercept <Link> to blog/another-page using a folder at blog/@modal/(.)[slug].

The purpose of this is to show summary modals for blog entries without navigating away from the current page.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

@zoogeny zoogeny added the bug Issue was opened via the bug report template. label Jul 11, 2023
@zoogeny zoogeny changed the title Interceptor for parallel routes with dynamic route not working within the same directory Intercepting parallel routes including dynamic route not working within the same directory Jul 11, 2023
zoogeny added a commit to zoogeny/next-js-interceptor-parallel-dynamic that referenced this issue Jul 11, 2023
Match the issue posted on vercel/next.js#52533
@Netail
Copy link
Contributor

Netail commented Jul 13, 2023

FORWARD: #49614

Facing the same issue with the following structure; (v13.4.9)

.
└── [locale]/
    ├── (program)/
    │   ├── programma/
    │   │   └── [slug]/
    │   │       └── [id]/
    │   │           └── page.jsx
    │   └── program/
    │       └── [slug]/
    │           └── [id]/
    │               └── page.jsx
    ├── @modal/
    │   ├── (.)programma/
    │   │   └── [slug]/
    │   │       └── [id]/
    │   │           └── page.jsx
    │   └── (.)program/
    │       └── [slug]/
    │           └── [id]/
    │               └── page.jsx
    └── layout.jsx

And on top of that; the locale param becomes undefined when visiting the route

@zoogeny
Copy link
Author

zoogeny commented Jul 13, 2023

@Netail Something weird is going on. I just pushed a new example to the linked reproduction repo with a similar structure to the one you posted. I don't have route groups but I did add a [locale] base route to my original repro and then kept the rest of the structure the same. For me, this nested structure surprisingly works. I get the intercept and the modal for both the good case and the previous bad case.

Maybe you can pull my repo and play around with it to get your reproduction case? It seems there is some combination of factors that is causing the intercept to fail that isn't totally clear.

@Netail
Copy link
Contributor

Netail commented Jul 14, 2023

Mmhm you're right, I wonder where the problem lays with our project...

@bryan-ttme
Copy link

Same issue here. I'm using next-intl and can't show modal with intercepted routes yet.

@Netail
Copy link
Contributor

Netail commented Jul 19, 2023

@Netail Something weird is going on. I just pushed a new example to the linked reproduction repo with a similar structure to the one you posted. I don't have route groups but I did add a [locale] base route to my original repro and then kept the rest of the structure the same. For me, this nested structure surprisingly works. I get the intercept and the modal for both the good case and the previous bad case.

Maybe you can pull my repo and play around with it to get your reproduction case? It seems there is some combination of factors that is causing the intercept to fail that isn't totally clear.

Alright found the issue which triggers the issue; generateStaticParams. Created a seperate issue for this; #52880

@kachkaev
Copy link
Contributor

I think I am facing a very similar bug, which I have reported in #53170. The main difference in my reproduction example is that it uses route groups too.

@jmacbhc
Copy link

jmacbhc commented Jul 28, 2023

Same issue with following folder structure:

blog
├── @modal/(.)[slug]
│   └── page.tsx
├── [slug]
│   └── page.tsx
└── layout.tsx
└── default.tsx
└── page.tsx

@smakman
Copy link

smakman commented Aug 3, 2023

I'm having the same issue with the following folder structure:

post
├── @modal
│   │── default.js
│   └── (.)[slug]
│       └── page.js
├── [slug]
│   └── page.js
└── layout.js
└── default.js
└── page.js

The following does work:

post
├── @modal
│   │── default.js
│   └── (..)post/[slug]
│       └── page.js
├── [slug]
│   └── page.js
└── layout.js
└── default.js
└── page.js

But this intercepts the route from pages outside of the [post] route group as well, which is not desired for our use case.

@FocusCookie
Copy link

I went into @zoogeny repo https://github.com/zoogeny/next-js-interceptor-parallel-dynamic and had a look into it. The structure of the blog actually solved my issue and I had a similar folder structure to Netail. Here is my new structure. Important to note that you need now a layout within your category/slug where the modal should be displayed, instead of within the app/layout.tsx. Thanks @zoogeny

Bildschirmfoto 2023-08-14 um 14 53 47

@rodrigomf24
Copy link

Are there any updates on this? I am having the same issue, the app is redirecting to the related path instead of mounting the intercepted route content.

@rodrigomf24
Copy link

I managed to fix this issue on my demo app, basically if you intercept from the root it ends up working, i.e.:

If you routes look like this inside the app folder, in this example assume you are using a blog and you want to show the blog post inside a modal when you are in the blog post feed.

app/
  blog/
     [postSlug]/
       page.tsx
     @postModal/
       (...)blog/             <--- this is were you intercept from the root instead of using (.) or (..)
         [postSlug]/
           page.tsx
     page.tsx

Intercepting inside a nested path using the same level or level above syntax didn't worked for me, but intercepting from the root works.

Screen Shot 2023-08-30 at 11 56 44 AM

@liqueflies
Copy link

liqueflies commented Sep 25, 2023

Hello,

my structure is like that

[lang]
- work
- - layout.tsx --> here is the {modal} children
- - detail
- - - [slug]
- - - - page.tsx
- - @modal
- - - default.tsx
- - - (.)detail
- - - - [slug]
- - - - - page.tsx

When I run npm run dev it works as expected but when I run npm run buld && npm run start and I go to the /en/work page into console the pages with /en/work/detail/my-work.rsc it throws 404 and hard reload to the page route without modal

Anyone experiencing this?
Any tip to solve?

@IvanRomanovski
Copy link

I could not get route matching to work with routes that started with dynamic parameter, eg. /[lang]/, but it did work if it started with static name , e.g. /locale/[lang].

This did not work me:

[lang]
-- page.tsx
@modal
- (.)[lang]
-- [lang]
--- page.tsx

But this did:

lang
- [lang]
-- page.tsx
@modal
- (.)lang
-- [lang]
--- page.tsx

Its probably just a regex happening behind the scenes between path and (.)whatever, so I am not surprised dynamic matching does not work...

@liqueflies
Copy link

I could not get route matching to work with routes that started with dynamic parameter, eg. /[lang]/, but it did work if it started with static name , e.g. /locale/[lang].

This did not work me:

[lang]
-- page.tsx
@modal
- (.)[lang]
-- [lang]
--- page.tsx

But this did:

lang
- [lang]
-- page.tsx
@modal
- (.)lang
-- [lang]
--- page.tsx

Its probably just a regex happening behind the scenes between path and (.)whatever, so I am not surprised dynamic matching does not work...

I achieved this repeating the structure inside modal for each language (I only have a small set of language, hope the same for you ATM)

- [lang]
- - page.tsx
- @modal
- - (.)en
- - - page.tsx
- - (.)it
- - - page.tsx

Hope it helps, little tricky but for now it does the job!

@brandensilva
Copy link

brandensilva commented Oct 6, 2023

Still an ongoing issue it looks like.

So if I want section/25/edit you would expect to utilize this folder structure based on the Next convention in the docs:

section
- [id]
--- edit
---- page.tsx
- @modal
-- (.)[id]
--- edit
---- page.tsx
- layout.tsx (this contains the modal slot reference)
- default.tsx (this returns null so the modal doesn't get stuck when leaving the route)

But this doesn't work. So instead we are left with what appears to be a few workarounds:

Work around 1:

You use (...) root segment as rodrigomf24 mentioned above: #52533 (comment). Your folder structure now looks like so:

section
- [id]
--- edit
---- page.tsx
- @modal
-- (...)section
--- [id]
---- edit
----- page.tsx
- layout.tsx (this contains the modal @slot reference)
- default.tsx (this returns null so the modal doesn't get stuck when leaving the route)

This solution is more akin to an absolute path. It isn't terrible if you don't have to traverse a long root app path in your @slot.

In my case my section actually lives much further down the route hierarchy so this just isn't worthwhile to nest segments so deeply under @modal. There is also the risk that it breaks if you add additional folder structures between the root and this without accounting for it.

Work around 2:

You use (..) instead of (...). This solution keeps that pesky section folder reference in our @modal but acts more like a relative path.

section
- [id]
--- edit
---- page.tsx
- @modal
-- (..)section
--- [id]
---- edit
----- page.tsx
- layout.tsx (this contains the modal @slot reference)
- default.tsx (this returns null so the modal doesnt get stuck when leaving the route)

Perhaps the biggest downside is it isn't as intuitive as it could be because it feels like we are breaking what should be the standard Next convention in the original example because I'd expect (..) to move me up two segments for example. And it kinda is but it feels a bit confusing or not as clear as it could be since you have to maintain the parent folder.

Workaround 3:

You avoid having (.) on a dynamic segment. You can achieve this with a nested folder like so.

section
- overview
-- [id]
--- edit
---- page.tsx
- @modal
-- (.)overview
--- [id]
---- edit
----- page.tsx
- layout.tsx (this contains the modal @slot reference)
- default.tsx (this returns null so the modal doesnt get stuck when leaving the route)

IvanRomanovski touches on this in his comment: #52533 (comment) even though his example is slightly different than mine.

This approach feels more in line with the Next convention but comes with a more polluted route path for users. It also might be difficult to come up with a nested segment for your use case.


Hopefully someone can tackle the core issue and allow dynamic segments to act as intercepted routes e.g) (.)[id]. For now these seem like the best solutions to working around this problem. If you spot any issues or typos with the solutions I've gathered to help save someone the hours of headache troubleshooting, let me know and I'll edit them.

thanks to everyone here for helping me work through this problem!

@jeanmolossi
Copy link

@Netail try to add a error handler

.
└── [locale]/
    ├── (program)/
    │   ├── programma/
    │   │   └── [slug]/
    │   │       └── [id]/
    │   │           └── page.jsx
    │   └── program/
    │       └── [slug]/
    │           └── [id]/
    │               └── page.jsx
    ├── @modal/
    │   ├── (.)programma/
    │   │   └── [slug]/
    │   │       └── [id]/
    │   │           └── page.jsx
    │   └── (.)program/
    │       └── [slug]/
    │           └── [id]/
    │               └── page.jsx
    └── layout.jsx
    └── error.jsx <-- It solved for me
// error.jsx
'use client'

export default function Error() {
    return (
        <div>Oops, error!</div>
    )
}

@jasonsilberman
Copy link

I am trying to do the same at the root level and then a dynamic route like /:id:

├── app
|  ├── @modal
|  |  ├── (.)
|  |  |  └── [id]
|  |  |     └── page.tsx
|  |  └── default.tsx
|  ├── [id]
|  |  └── page.tsx
|  ├── default.tsx
|  ├── layout.tsx
|  └── page.tsx

and it is not working correctly at all. I made a repro of the original next gram app showing the behavior here: https://github.com/jasonsilberman/nextgram13-root-path

@ZeroCho
Copy link

ZeroCho commented Oct 21, 2023

@jasonsilberman You should make (.)[id] not (.)/[id].
(.)[id] is a name of a single directory.

@renzo4web
Copy link

@Netail try to add a error handler

.
└── [locale]/
    ├── (program)/
    │   ├── programma/
    │   │   └── [slug]/
    │   │       └── [id]/
    │   │           └── page.jsx
    │   └── program/
    │       └── [slug]/
    │           └── [id]/
    │               └── page.jsx
    ├── @modal/
    │   ├── (.)programma/
    │   │   └── [slug]/
    │   │       └── [id]/
    │   │           └── page.jsx
    │   └── (.)program/
    │       └── [slug]/
    │           └── [id]/
    │               └── page.jsx
    └── layout.jsx
    └── error.jsx <-- It solved for me
// error.jsx
'use client'

export default function Error() {
    return (
        <div>Oops, error!</div>
    )
}

Work!! thanks

@renovatt
Copy link

renovatt commented Nov 3, 2023

"next": "13.4.19",

I'm facing a slightly strange problem, I don't know if I'm missing something.

I have a project that is already working normally, the dynamic routes together with route interception, everything is ok. The folder structure is as follows:
Captura de tela 2023-11-03 112111

link: https://portfolio-renovatt.vercel.app/views/projects
Note: the route is being intercepted, the background remains the same.

But I'm trying to use hidden routes, (views), putting them in parentheses, and following the same structure, following the same pattern, route interception doesn't work correctly.

An error occurs where the original route and the intercepted route are called together.

I changed the structures a few times and found these two repositories, which I found in other issues:
#49614
https://github.com/vetledv/repro-intercept/blob/main/src/app/(some-group)/%40modal/(.)photos/%5Bid%5D/page.tsx
https://github.com/Apestein/nextflix/blob/main/src/app/(main)/%40modal/(.)show/%5Bid%5D/page.tsx

They are doing it like this, placing @modal inside (main), but when I do this, the route appears in the URL , however the screen is not changed, in this case my component does not appear, but if I update the page the original route happens, but just by placing (views) as the parent folder, the intercepted route happens, but with the same error as before , where the original route and the intercepted route are called together.

Captura de tela 2023-11-03 111949
Captura de tela 2023-11-03 111653

I don't understand why this happens, can anyone help me?

@yuuhn
Copy link

yuuhn commented Nov 6, 2023

reposting #52880 (comment)
I'm experiencing the same issue. My folder structure is as follows:

app
  [user]
    page.tsx
  @profile
    (.)[user]
      page.tsx
  layout.tsx
  page.tsx

next.js 14.0.1

@graup
Copy link

graup commented Nov 6, 2023

None of the workarounds here is working for me. Are the docs missing some information? Are dynamic routes not supported? Isn't this an obvious use case for parallel routes? Paging @leerob

What I wanted to do is to show both the items page as well as the detail page in a slide-in sheet modal.

app
  settings
    items
      @sheet 
        [id]
          page.tsx
      page.tsx
      layout.tsx

So /settings/items should show the list of items, and /settings/items/123 shows both the list as well as the detail view in a modal. Currently getting a 404 for the latter.


Edit: I somehow figured it out! At least for my case.

Here's my file structure:
Screenshot 2023-11-07 at 0 18 34

As far as I understand, all those files are necessary. In particular, it wouldn't work without a default.tsx on both of the parallel routes (parent and child).

// ./default.tsx
import { default as Page } from "./page";
// Needed to support hard reload on a nested parallel route
export default Page;
// ./@sheet/default.tsx
export default function Default() {
  return null;
}
// layout.tsx
import { ReactNode } from "react";
export default function Layout({
  children,
  sheet,
}: {
  children: ReactNode;
  sheet: ReactNode;
}) {
  return (
    <>
      {children}
      {sheet}
    </>
  );
}

The only thing that doesn't work is the catchall route to remove the parallel route again, but there are some workarounds for that mentioned here.

@kuki-quupi
Copy link

kuki-quupi commented Nov 7, 2023

I'm experiencing the same problem as above stated.

Got folder structure like this:

app

  • [chatID]
    • page.tsx
  • @chat
    • (.)[chatID]
      • page.tsx
  • layout.tsx
  • page.tsx

And i tried implementing parallel route without intercepting route like this:

app

  • @chat
    • (.)[chatID]
      • page.tsx
  • layout.tsx
  • page.tsx

But neither of the concepts is working.

When i do parallel route without dynamic route it works fine.
This works as shown in docs:

app

  • @chat
    • page.tsx
  • layout.tsx
  • page.tsx

Next.js v 14.0.1

@renovatt
Copy link

renovatt commented Nov 7, 2023

"next": "13.4.19",

I'm facing a slightly strange problem, I don't know if I'm missing something.

I have a project that is already working normally, the dynamic routes together with route interception, everything is ok. The folder structure is as follows: Captura de tela 2023-11-03 112111

link: https://portfolio-renovatt.vercel.app/views/projects Note: the route is being intercepted, the background remains the same.

But I'm trying to use hidden routes, (views), putting them in parentheses, and following the same structure, following the same pattern, route interception doesn't work correctly.

An error occurs where the original route and the intercepted route are called together.

I changed the structures a few times and found these two repositories, which I found in other issues: #49614 https://github.com/vetledv/repro-intercept/blob/main/src/app/(some-group)/%40modal/(.)photos/%5Bid%5D/page.tsx https://github.com/Apestein/nextflix/blob/main/src/app/(main)/%40modal/(.)show/%5Bid%5D/page.tsx

They are doing it like this, placing @modal inside (main), but when I do this, the route appears in the URL , however the screen is not changed, in this case my component does not appear, but if I update the page the original route happens, but just by placing (views) as the parent folder, the intercepted route happens, but with the same error as before , where the original route and the intercepted route are called together.

Captura de tela 2023-11-03 111949 Captura de tela 2023-11-03 111653

I don't understand why this happens, can anyone help me?

"next": "13.4.19",

I'm facing a slightly strange problem, I don't know if I'm missing something.

I have a project that is already working normally, the dynamic routes together with route interception, everything is ok. The folder structure is as follows: Captura de tela 2023-11-03 112111

link: https://portfolio-renovatt.vercel.app/views/projects Note: the route is being intercepted, the background remains the same.

But I'm trying to use hidden routes, (views), putting them in parentheses, and following the same structure, following the same pattern, route interception doesn't work correctly.

An error occurs where the original route and the intercepted route are called together.

I changed the structures a few times and found these two repositories, which I found in other issues: #49614 https://github.com/vetledv/repro-intercept/blob/main/src/app/(some-group)/%40modal/(.)photos/%5Bid%5D/page.tsx https://github.com/Apestein/nextflix/blob/main/src/app/(main)/%40modal/(.)show/%5Bid%5D/page.tsx

They are doing it like this, placing @modal inside (main), but when I do this, the route appears in the URL , however the screen is not changed, in this case my component does not appear, but if I update the page the original route happens, but just by placing (views) as the parent folder, the intercepted route happens, but with the same error as before , where the original route and the intercepted route are called together.

Captura de tela 2023-11-03 111949 Captura de tela 2023-11-03 111653

I don't understand why this happens, can anyone help me?

I managed to fix this problem, solution:
I created the parallel route @modal outside the hidden route (views) and just placed the dynamic route following the same path, directly, without saying that it is inside the (views) route, this way it will actually be intercepted, this way I can normally call it /project/id.

I tested several ways, and I saw that calling the @modal route inside the (views) route was calling both routes at the same time, and the other way was calling the route but the component was not being used intercepted.

I hope this can help others with the same problem.
Captura de tela 2023-11-07 153738
Captura de tela 2023-11-07 153930

kodiakhq bot pushed a commit that referenced this issue Nov 8, 2023
This PR fixes the bug in which interception routes of the form `(.)[param]` would not intercept navigations.

The bug happened because we would not create a dynamic route matcher for the intercepted route, so we would never match the correct dynamic route when hitting the server, falling back to the base one. 

The fix consists of fixing the logic that checks for a dynamic route so that it checks the correct path when handling an interception route.

There's probably a better fix here, advice welcome

fixes #52533
Copy link
Contributor

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.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template. locked
Projects
None yet
Development

Successfully merging a pull request may close this issue.