skip to content

Can a single Next.js project contain both a pages/ directory and an app/ directory at the same time, and what happens if the two define the same URL path?

level: juniorimportance: should knowfreq 54%

answer

  1. coexistence is the supported migration path
  2. one config, one middleware, two route trees
  3. same URL in both is a build error
  4. crossing between them is not a soft navigation
  5. client state does not survive the boundary

basics

~20 s

Yes — Next.js supports both directories in one project so teams can migrate route by route. The App Router takes priority, but two routes that resolve to the same URL are rejected at build time with a conflict error rather than one silently winning.

solid answer

~40 s

Coexistence is the supported migration path, not a hack: `pages/` and `app/` build into one application, sharing `next.config`, `public/`, and a single root `middleware` file. The App Router takes priority over the Pages Router, but you are not allowed to rely on that for the same path — if `pages/about.tsx` and `app/about/page.tsx` both exist, the build fails with a conflicting-file error naming both. So migration is genuinely one route at a time: you move a route, delete the old file, ship. The thing juniors miss is that the two routers do not share a client-side router. Navigating from an `app/` route to a `pages/` route is a full document load, not a soft client-side transition, so any in-memory state is lost and the boundary is visibly slower.

go deeper

for a junior

Say clearly that both directories can exist in one project, that this is the supported way to migrate gradually, and that two files resolving to the same URL cause a build error.

for a middle

Explain what is shared (config, public assets, middleware) versus duplicated (shells, data-fetching idioms, router hooks), and that crossing between routers is a full page load.

for a senior

Reason about the operational fallout: which client state dies at the boundary, how to sequence route moves so users rarely cross it, and how to keep providers from double-initialising.

for a principal

Treat coexistence as a cost with a clock on it — set a definition of done and a deadline for the half-migrated window, and decide what new work is allowed to land in pages/ in the meantime.

## Both directories, one application Next.js explicitly supports running the Pages Router and the App Router side by side in the same project. That is how the framework intends large applications to migrate — incrementally, route by route, shipping continuously — rather than through a big-bang rewrite on a long-lived branch. Mechanically, both directories are compiled into one build output. The project keeps one `next.config` file, one `public/` directory, one root `middleware` file that runs for matching requests regardless of which router serves them, and one dependency tree. There is no per-router configuration split. ## The routing rule Routes from the two directories go into one route table. The documented precedence is that the App Router takes priority over the Pages Router. But you should not design around that precedence, because Next also refuses the ambiguous case: if a path resolves in both directories, the build fails with a conflict error that names the two offending files. A `pages/about.tsx` and an `app/about/page.tsx` cannot both exist. That error is a feature. It means "migrate a route" is an atomic operation — create the new file, delete the old one, in the same commit — and there is no state where a route is half-moved and you are guessing which version production is serving. One detail worth knowing: `pages/api/*` API routes are unaffected by all of this and keep working exactly as before. They can stay while UI routes move, and they only conflict if you create a route handler at the same path in `app/`. ## The boundary between the two routers This is the part that surprises people, and the part interviewers probe. Inside one router, client-side navigation is a soft transition: the framework fetches what it needs and swaps the UI without a document reload, keeping the JavaScript heap intact. Across the boundary, that does not happen. Going from an `app/` route to a `pages/` route — or the reverse — is a **hard navigation**: a full document load, a fresh JavaScript runtime, providers re-created, and any in-memory client state discarded. The practical consequences during a partial migration: - A cart, a wizard's form state, or an open WebSocket held in a client-side store is destroyed whenever the user crosses the boundary. If it must survive, it has to be in a cookie, `sessionStorage`, or on the server. - Crossing the boundary costs a full page load's worth of latency. Users notice it on paths they walk repeatedly. - Providers declared in both the root layout and `_app` run twice over a session — once per router — which duplicates analytics initialisation and any "on app start" side effect if you are not careful. This is why the migration order is usually planned around *clusters of routes that link to each other*, not around "easiest file first": you want the boundary to fall where users rarely cross it. ## What is duplicated while both live During coexistence you maintain two of several things: - Two shells: `pages/_app.tsx` plus `pages/_document.tsx` for the old side, `app/layout.tsx` for the new one. Global CSS is imported in both. - Two data-fetching idioms: `getServerSideProps`/`getStaticProps` in `pages/`, async Server Components in `app/`. - Two navigation APIs: `useRouter` from `next/router` under `pages/`, and the hooks from `next/navigation` under `app/`. Importing the wrong one is a runtime error rather than a type error in plain JS, and it is one of the most common migration bugs. None of that is fatal, but all of it is carrying cost. The reason to state it plainly in an interview is that it argues for keeping the half-migrated window short and deliberate, rather than treating indefinite coexistence as a comfortable resting state. ## The short answer to give Yes, they coexist; it is the supported incremental path; overlapping paths are a build error, not a precedence puzzle; shared config and middleware apply to both; and the real operational cost is the hard navigation at every boundary crossing plus duplicated shell code on both sides.

  • Does the root middleware file apply to routes in both directories, or do you need one per router?
    One root middleware file covers the whole application. It runs for requests matching its matcher regardless of whether the request is ultimately served by an `app/` route or a `pages/` route, which is convenient during migration — auth gating written once keeps working as routes move between the two trees.
  • What happens to a Zustand or Redux store's contents when a user navigates from an app/ route to a pages/ route?
    It is lost. The crossing is a full document load, so the JavaScript runtime is torn down and the store is re-initialised empty on the other side. Anything that must survive needs to be persisted outside memory — a cookie, storage, or the server — or the two routes need to be on the same side of the boundary.
  • Can pages/api routes stay while the UI routes move to app/?
    Yes. API routes under `pages/api` are independent of the UI router migration and keep working. You only hit a conflict if you also create a route handler at the same path under `app/`. Many teams move the UI first and convert API routes to route handlers later, or leave them indefinitely.

saying these in an interview costs you the question

  • Says you must migrate the whole app at once
  • Thinks app/ silently overrides a conflicting pages/ route at runtime
  • Assumes navigation between the routers is a soft client-side transition
  • Expects client state or context to survive crossing the boundary
  • Thinks each router needs its own config or middleware file

context