cacheComponents and intercepting routes problem

I am having a problem with intercepting routes and especially modals to work in Next.js 16 its latest version. I mean, everything works just fine in production, but only if I disable the “cacheComponents” feature, and this is how it works now:

Once you go to the “Notes” section and sign in as a demo user, either by clicking the “Notes” icon in the nav bar or by directly going to:

You will be presented with a bunch of example notes where you can click on any note to view its details, add a new note, or edit the existing note. The whole interface depends heavily on modals/intercepting routes so that the user always sees their collection of notes. As you can see, it works just fine—the modals are being presented as advertised. Once I enable the “cacheComponents” feature, it still works fine, but only on my local machine. Things change when I deploy to Vercel.

Now, only one intercepting route opens a modal correctly “New Note” at:
app/@notes/(.)notes/new/page.tsx

here is the code:

// react
import { Suspense } from "react";

// components
import NoteModal from "@/features/notes/components/note-modal";
import BrowseBar from "@/features/notes/components/browse-bar";
import NewNoteForm from "@/features/notes/components/NewNoteForm";

// assets
import { DocumentPlusIcon } from "@heroicons/react/24/outline";

// Page remains the fast, static shell
export default function Page() {
  return (
    <Suspense fallback={<PageSkeleton />}>
      <PageContent />
    </Suspense>
  );
}

// This new async component contains the dynamic logic
async function PageContent() {
  return (
    <NoteModal icon={<DocumentPlusIcon className="size-11 flex-none" />} browseBar={<BrowseBar kind="note-new" />}>
      <NewNoteForm inNoteModal />
    </NoteModal>
  );
}

function PageSkeleton() {
  return null;
}

Any other intercepting route fails with this message for example in the browser’s console window:

848cf2c4b414a902.js:1
GET https://total-recall-ai-git-cc-remis-projects-738a757c.vercel.app/notes/8f149149-f54c-4255-9824-a7e68073ed46?_rsc=1vq60 404 (Not Found)

Here is one of the problematic intercepting routes:
app/@notes/(.)notes/[id]/page.tsx

here is the code:

// react
import { Suspense } from "react";

// services, features, and other libraries
import { validatePageInputs } from "@/lib/helpers";
import { NoteDetailsPageSchema } from "@/features/notes/schemas/noteDetailsPage";

// components
import NoteModal from "@/features/notes/components/note-modal";
import BrowseBar from "@/features/notes/components/browse-bar";
import NoteDetails from "@/features/notes/components/NoteDetails";

// assets
import { DocumentIcon } from "@heroicons/react/24/outline";

// Page remains the fast, static shell
export default function Page({ params, searchParams }: PageProps<"/notes/[id]">) {
  return (
    <Suspense fallback={<PageSkeleton />}>
      <PageContent params={params} searchParams={searchParams} />
    </Suspense>
  );
}

// This new async component contains the dynamic logic
async function PageContent({ params, searchParams }: PageProps<"/notes/[id]">) {
  // Safely validate next.js route inputs (`params` and `searchParams`) against a zod schema; return typed data or trigger a 404 on failure
  const {
    params: { id: noteId },
  } = await validatePageInputs(NoteDetailsPageSchema, { params, searchParams });

  return (
    <NoteModal icon={<DocumentIcon className="size-11 flex-none" />} browseBar={<BrowseBar kind="note-details" />} noteId={noteId}>
      <NoteDetails noteId={noteId} />
    </NoteModal>
  );
}

function PageSkeleton() {
  return null;
}

The only difference from the previous intercepting route is this line which “awaits” stuff:

// Safely validate next.js route inputs (`params` and `searchParams`) against a zod schema; return typed data or trigger a 404 on failure
const {
params: { id: noteId },
} = await validatePageInputs(NoteDetailsPageSchema, { params, searchParams });

I noticed that even the simplest “await” breaks the whole intercepting route. By the way, you can access the whole code and see the entire file structure at:

There’s another community post with 404 debugging tips that might be helpful. Please give these solutions a try and let us know how it goes.

A human should be around soon to offer more advice. But you can also get helpful information quickly by asking v0.

Please forgive me if my previous post was a bit confusing and chaotic. This time, I will try to do a better job of describing the issue and providing some screenshots as well.

First, I would like to provide evidence of a successful build and the version that functions perfectly on my local machine.

Build Output

The “cacheComponents” feature is enabled, and, as you can see, some of the pages are “partially prerendered,” which is the expected behavior. This includes our intercepting routes for displaying a modal: “◐ /(.)notes/[id]”, “◐ /(.)notes/[id]/edit”, and “○ /(.)notes/new” (the latter is fully static):

Local Deployment

The screenshots below show that everything works as it should. You can make a new note, see existing notes, and edit them. The interface is offered in the form of modals to prevent users from losing their browsing context or being moved to a whole new page:



Remote Deployment

After deploying to Vercel, I am having a series of really problematic issues. Again, this only impacts the “intercepting routes” capability and occurs when “cacheComponents” is enabled. If you manually refresh your browser, the route will no longer be intercepted and will render appropriately, but the benefit of having modals is completely lost:

NEW NOTE crashes


NOTE DETAILS works, but with a bunch of network-related issues


What Have I Learned So Far

I have been trying everything I could think of. I even converted all my “intercepting routes” to be client-side pages so that their code can be immediately downloaded onto the client’s browser. It seems that after the remote deployment, the code chunks (for server components) cannot be retrieved from the remote server for some reason or another—404 happens quite a lot.

Any advice or suggestions from the community would be greatly welcomed!

Again, the link to the entire GitHub repo of this project is at:
https://github.com/remi-k-work/total-recall-ai

Thank you all for the help! As I mentioned, I have been struggling with the issue of intercepting routes working together, particularly with the “cacheComponents” feature being enabled. In the back of my mind I knew that I must have been making a silly and stupid “mistake,” but I could not have figured it out.

The confusing and misleading part was that when I was originally developing the app with the “cacheComponents” turned off, I would never get any kind of errors even after deploying. The core issue, which was the wrong file structure (more about this in a second), did not stop the app from working even after deploying.

After enabling the “cacheComponents” feature (I am still using the wrong file structure), everything functions properly on my development machine. However, after deployment, it begins to crash with “404” errors. Link prefetching fails to work, and modals sometimes open and sometimes do not.

To avoid boring you further and embarrassing myself in the process, here is the key section from the excellent Next.js documentation that I should have reviewed more often:

Specifically the part:

Additionally, since children is an implicit slot, you also need to create a default.js file to render a fallback for children when Next.js cannot recover the active state of the parent page.

So, adding one little “default.tsx” below solved all my problems:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.