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:
https://total-recall-ai.vercel.app/
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:
https://total-recall-ai.vercel.app/notes
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:
// reactimport { Suspense } from "react";
// componentsimport NoteModal from "@/features/notes/components/note-modal";import BrowseBar from "@/features/notes/components/browse-bar";import NewNoteForm from "@/features/notes/components/NewNoteForm";
// assetsimport { DocumentPlusIcon } from "@heroicons/react/24/outline";
// Page remains the fast, static shellexport default function Page() { return ( <Suspense fallback={<PageSkeleton />}> <PageContent /> </Suspense> );}
// This new async component contains the dynamic logicasync 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:
// reactimport { Suspense } from "react";
// services, features, and other librariesimport { validatePageInputs } from "@/lib/helpers";import { NoteDetailsPageSchema } from "@/features/notes/schemas/noteDetailsPage";
// componentsimport NoteModal from "@/features/notes/components/note-modal";import BrowseBar from "@/features/notes/components/browse-bar";import NoteDetails from "@/features/notes/components/NoteDetails";
// assetsimport { DocumentIcon } from "@heroicons/react/24/outline";
// Page remains the fast, static shellexport 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 logicasync 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 failureconst {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:




