AI Unified Process UI layer - Next.js App Router pages on an API layer, and their component tests
94%
Does it follow best practices?
Impact
96%
1.00x1 of 3 eval scenarios. Add 2 more for a full score.
Passed
No findings from the security scan
This is a lookup used by /nextjs-ui and /nextjs-ui-test before writing any code. It answers one question:
where does the web application live, and which of its conventions must new code match? It says nothing about
the backend: the API layer may be NestJS, Spring Boot, or anything else that speaks JSON over HTTP, and its contract
reaches this layer through the Handover.
Never assume; always run this detection first. Router style is the unforgiving answer: a src/pages directory in an
App Router project is not inert, because Next.js tries to route it.
| # | Question | Signal | Consequence if wrong |
|---|---|---|---|
| 1 | Web app root | The UI row's Location in docs/architecture/stack.md; otherwise the workspace whose package.json has next in dependencies | Code lands in the wrong app |
| 2 | Router style | src/app/ (or app/) present → App Router; src/pages/ only → Pages Router | A second conflicting router |
| 3 | Route indirection | Whether existing page.tsx files hold the page markup or re-export a component from elsewhere | Convention split across the codebase |
| 4 | API rewrite | rewrites() in next.config.* | Requests that 404 in one environment |
| 5 | Fetch client | A module with apiGet/apiPost or similar, and any hook wrapping it | Error handling and base path bypassed |
| 6 | Shared contract package | A workspace package imported by the web app that exports request and response types | Duplicated, drifting types |
| 7 | Component library | shadcn/ui, Tailwind, lucide-react, or another library in package.json | A foreign look, a second library |
These skills build the App Router. In a Pages Router project, stop and report it: the conventions here do not apply.
Read the UI row of docs/architecture/stack.md. Without one, read the repo-root package.json, expand each glob of
workspaces, and resolve the root from the dependency signal, not from a directory name. With no workspaces field,
search for next.config.*. State the root you found before writing anything, so a wrong guess is visible
immediately rather than after a dozen files have landed in the wrong place.
Check what a route file actually contains:
// Direct: the route file holds the page
export default function ProductsPage() {
return <main>…</main>;
}// Indirect: the route file is a thin wrapper
'use client';
import { ProductsPage } from '../../views/ProductsPage';
export default function Page() {
return <ProductsPage />;
}Where the project uses indirection, new pages follow it: a thin wrapper at the route, the markup in a component beside
its siblings. /nextjs-ui-test must target the component that holds the markup, because a test rendering the wrapper
asserts nothing.
A directory named views (or screens, or containers) is a deliberate choice to avoid src/pages, which the
Pages Router would claim. Do not "tidy" it into src/pages.
The browser calls relative /api/... paths, and a rewrite in next.config.* forwards them to the API origin. Read
the rewrite and compare it with the base path of the API contract section of the stack file. With no rewrite yet, add
one that reads the origin from an environment variable with a local default, and name the variable in your report:
// next.config.ts
const nextConfig = {
async rewrites() {
const origin = process.env.API_ORIGIN ?? 'http://localhost:3001';
return [{ source: '/api/:path*', destination: `${origin}/api/:path*` }];
},
};
export default nextConfig;The default matters: without it an unset variable gives the destination undefined/api/:path* and next build stops
with Invalid rewrite found. next build fixes the origin: a rewrite is evaluated at build time, so changing
API_ORIGIN when starting the built application changes nothing. Set it at build time, one build per environment. A
project that ships one image to several environments needs the origin at runtime, through a Route Handler under
src/app/api/ that forwards the request; that is a decision to ask about, not a default.
Find one implemented page and copy its shape. The table tells you where things live; an existing feature tells you how this team writes them.
fetch in a project that has a
client module bypasses its error handling and base-path logic.react-hook-form, a schema library such as zod, or plain controlled state?/nextjs-ui.Fall back to these defaults rather than inventing a structure, and say which ones you applied, so the first page's conventions are a visible decision rather than an accident the rest of the codebase inherits:
src/app/<route>/page.tsx, with no separate view directory.src/types/<entity>.ts.fetch against relative /api/... paths.useState; no form or state library.