Skip to content

Intercepting routes don’t work with route groups #53170

@kachkaev

Description

@kachkaev

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.5.0: Thu Jun  8 22:22:20 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T6000
    Binaries:
      Node: 18.16.0
      npm: 9.5.1
      Yarn: 1.22.19
      pnpm: 8.6.10
    Relevant Packages:
      next: 13.4.13-canary.1
      eslint-config-next: 13.4.13-canary.1
      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/kachkaev/next-js-intercepting-routes-in-groups-mwe

Here is the ‘product spec’:

The app is a photo browser, similar to nextgram.
There are three navigation pages:

  1. Home page (recent photos)
  2. top photos
  3. best photo

When users navigate to a photo on the first two pages, it should open in a modal.
When users navigate to a photo on the third page, it should open in a new page, not in a modal.
Each photo page has previous / next links. Neighbouring photos open in the same way: in a modal or in a new page*.


* When I add previous / next links in the nextgram app, neighbouring photos always open in a modal. This creates modals on the standalone photo page, which is what I am trying to avoid:

nextrgram-previous-next.mp4

To Reproduce

  1. Clone the repo:

    git clone https://github.com/kachkaev/next-js-intercepting-routes-in-groups-mwe.git
    cd next-js-intercepting-routes-in-groups-mwe
    pnpm install
    pnpm dev
  2. Inspect the folder structure:

    app/
    ├── (no-modal)
    │   ├── best-photo
    │   │   └── page.tsx
    │   ├── layout.tsx
    │   └── photo
    │       └── page.tsx
    ├── (with-modal)
    │   ├── @modal
    │   │   ├── (...)photo
    │   │   │   └── page.tsx
    │   │   ├── page.tsx
    │   │   └── top-photos
    │   │       └── page.tsx
    │   ├── layout.tsx
    │   ├── page.tsx
    │   └── top-photos
    │       └── page.tsx
    └── layout.tsx

    Here is the technical solution I have come up with based on the docs:

    • There is a root layout for global navigation, CSS reset, custom fonts, etc.
    • There are two route groups called (no-modal) and (with-modal).
    • Home page and ‘top photos’ page are in the (with-modal) group.
    • The layout file in this group contains a slot called @modal which renders a photo page using an intercepting route.
    • The ‘best photo’ page is in the (no-modal) group, together with the photo page itself.
    • In addition to the intercepting route for a photo, app/(with-modal)/@modal also contains dummy routes for the home page and the ‘top photos’ page to avoid 404s. I know it is possible to add default or catch-all routes to this folder too, but doing so would blur the distinction between the two route groups and thus make the reproduction a bit less vivid.

    The reproduction example does not use dynamic routes for simplicity. Photo ids are encoded in a GET param. Going for photo/[id] produces the same results (see below).

  3. Open http://localhost:3000 and navigate between the home page and the ‘top photos’ page. Click on individual photo links within these pages and observe them opened in the modal, as expected. We are in the same route group called (with-modal), all works fine so far.

    happy-path.mp4
  4. Broken scenario 1: While on a photo page, press refresh in the browser. Observe that the photo page is opened in a new page, not in a modal. This is expected. Now try navigating to a previous or a next photo and observe an error:

    Unhandled Runtime Error
    Error: notFound() is not allowed to use in root layout
    
    broken-scenario-1.mp4
  5. Broken scenario 2: Navigate to the ‘best photo’ page and observe that it uses (no-modal) layout, as expected. Navigate to a photo page and observe the same error as above.

    broken-scenario-2.mp4
  6. Broken scenario 3: Open the photo page and observe that it uses (no-modal) layout, as expected. Make a small tweak in /app/(no-modal)/photo/page.tsx to trigger hot reloading. Observe notFound() error again:

    broken-scenario-3

Describe the Bug

Seems like Next.js is unable to deal with intercepting routes and route groups at the same time. Presence of app/(with-modal)/@modal/(...)photo/page.tsx breaks navigation to /photo from pages where this route is not intercepting.

The same happens for dynamic routes (i.e. when /photo?id=123 is replaced with /photo/123). See the PR #1 in the reproduction repo.

  →    

Expected Behavior

I would expect that the app would work according to product spec described earlier. The folder structure I have come up with seems to match Next.js docs for the listed requirements. Avoiding modals at least on the photo page is crucial and I am not sure how to do this without using route groups and intercepting routes.

Perhaps, this is just a matter of docs and I am just missing something. I have been trying various workarounds for quite a few hours last weekend but have not solved the problem no matter what I have tried. Hope that the reproduction example is helpful.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

I have not tried deploying this app yet, but if I try pnpm build && pnpm start, navigation remains broken in the same scenarios as during pnpm dev. There are two key differences:

  1. Error message says:
    Application error: a server-side exception has occurred (see the server logs for more information).
    Digest: NEXT_NOT_FOUND
    
  2. Unlike in pnpm dev, browser URL does not update when a broken link is followed.
pnpm-build-start.mp4

PS: Despite this situation and some level of frustration caused by it, I find the new router pretty awesome. So let me use this bug report as an opportunity to thank the Next.js community for working on it! I know that some developers struggle with the new model because of the things they’ve got used to, but I do see a lot of value in the change! Epic stuff! 👏

Metadata

Metadata

Assignees

No one assigned

    Labels

    Parallel & Intercepting RoutesRelated to Parallel and/or Intercepting routes.bugIssue was opened via the bug report template.locked

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions