How AI Code Generators Create Circular Imports in Next.js (and How to Spot Them)
AI is fast at files and slow at architecture. The result is circular dependency spaghetti that `next dev` hides until production build explodes.
AI code generators are excellent at producing plausible files quickly and terrible at preserving module boundaries. In Next.js apps, that gap shows up as circular imports: `auth.ts` imports `db.ts`, `db.ts` imports `session.ts`, and `session.ts` imports `auth.ts` again. The app still runs in `next dev`. Then Vercel build fails with a TypeScript or dynamic import error nobody can reproduce locally.
Five AI patterns that create cycles
Pattern one is the barrel file. AI loves `export * from "./foo"` because it makes imports look clean. One new export and suddenly `lib/index.ts` pulls in a server module that imports a client component that imports the barrel again. Pattern two is the config hub: the model creates `lib/env.ts` or `lib/config.ts` and wires every feature through it, then adds feature-specific imports back into config for "convenience."
Pattern three is the provider triangle. A new `AuthProvider` imports `useSession` from `hooks/use-session.ts`, but the hook imports the provider context from the same file tree root. Pattern four is duplicate utilities: two files get nearly identical `formatDate` helpers, then a refactor merges usage and creates a bidirectional import. Pattern five is the server/client boundary violation — especially common when AI adds `"use client"` to a file that used to be server-only without untangling the import graph.
Why `next dev` lies to you
Development mode evaluates modules lazily and tolerates initialization order quirks that production bundling does not. A circular graph can appear to work until a specific export is first accessed during SSR, tree-shaking removes a side effect the cycle depended on, or TypeScript emits a value import where `import type` was required. That is why teams search for "next js dynamic import typescript error" after green local runs and red Vercel deploys.
Both belong in CI. They optimize for different questions.
| Tool | Best for | Tradeoff |
|---|---|---|
| madge | Fast circular dependency reports and SVG graphs for quick PR triage. | Less opinionated about forbidden import rules (server/client, layer boundaries). |
| dependency-cruiser | Policy-as-code: ban cycles, enforce folder layers, flag orphan modules. | Heavier setup; worth it once the repo has import architecture rules. |
| next build | Ground truth for what production will compile and bundle. | Slower; run on changed apps or in sandbox review, not every save. |
Run graph analysis on the PR diff path, then confirm with `next build` before merge. Dev server green is not evidence.
Fix order that actually works
- 1Can this import be type-only?Replace value imports with `import type` when only types cross the boundary. This breaks many TypeScript emit cycles without runtime changes.
- 2Are types mixed with runtime code?Extract shared interfaces to `types.ts` or `*.types.ts` files that import nothing from feature modules.
- 3Is a barrel file involved?Import from the concrete module path instead of `index.ts`. Remove or narrow barrel re-exports on hot paths.
- 4Is initialization order the real issue?Move shared constants or factory functions to a leaf module both sides can import. Avoid top-level side effects in imported files.
- 5Did the fix survive production build?Re-run madge or dependency-cruiser and `next build`. A cycle "fixed" only in dev is not fixed.
Common cycle shapes in Next.js
Left: barrel loop. Right: type-only fix.
// lib/index.ts
export * from './auth'
export * from './db'
// lib/auth.ts
import { getUser } from '@/lib' // pulls db → session → auth
// lib/db.ts
import { getSession } from '@/lib/auth'PR-time detection
Circular imports accumulate across AI-assisted refactors. The reliable gate is PR-time: graph check on the diff, required `next build`, and review comments that flag new barrels or config-hub imports. Static AI review cannot prove the bundler agrees — that requires running the build.
Critique runs sandbox verification on pull requests so TypeScript compile errors and Next.js build failures surface before merge, not after Vercel emails the team. Pair graph tooling in CI with runtime proof when AI generators are writing most of the imports.
FAQ
Catch circular imports before Vercel does
Install Critique on GitHub and run sandbox build verification on the next AI-generated PR. Graph tools find cycles; runtime proof confirms the fix.
Start free