Skip to main content
RapidDev - Software Development Agency
cursor-tutorial

How to fix broken imports from Cursor

Cursor frequently generates circular imports in Node.js projects because it resolves imports based on type proximity rather than module dependency graphs. This tutorial shows how to add import rules to .cursorrules, structure your project with a clear dependency hierarchy, and use Cursor to detect and fix existing circular dependencies.

What you'll learn

  • Why Cursor generates circular imports and how to prevent it
  • How to set up .cursorrules with import hierarchy rules
  • How to detect existing circular imports using Cursor
  • How to restructure code to break circular dependency chains
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner7 min read10-15 minCursor Free+, Node.js/TypeScript projectsMarch 2026RapidDev Engineering Team
TL;DR

Cursor frequently generates circular imports in Node.js projects because it resolves imports based on type proximity rather than module dependency graphs. This tutorial shows how to add import rules to .cursorrules, structure your project with a clear dependency hierarchy, and use Cursor to detect and fix existing circular dependencies.

Fixing broken imports from Cursor in Node.js projects

Circular imports cause undefined values, mysterious runtime errors, and test failures that are hard to debug. Cursor generates them frequently because it picks the nearest matching export without considering the full dependency graph. This tutorial teaches you how to configure Cursor to respect your module hierarchy and fix existing circular dependencies.

Prerequisites

  • Cursor installed with a Node.js or TypeScript project
  • Project with multiple modules that import from each other
  • Basic understanding of ES modules or CommonJS imports
  • Familiarity with Cmd+L and Cmd+I in Cursor

Step-by-step guide

1

Detect existing circular imports

Use Cursor Chat to analyze your project's import graph and find circular dependencies. This gives you a map of existing problems before setting up prevention rules.

Cursor Chat prompt
1// Cursor Chat prompt (Cmd+L, Ask mode):
2// @codebase Analyze the import statements across all
3// .ts and .js files in src/. Identify any circular
4// import chains where module A imports from B and B
5// imports from A (directly or through intermediaries).
6// List each circular chain with the full import path.
7
8// You can also use a CLI tool for verification:
9// npx madge --circular --extensions ts src/

Expected result: A list of circular import chains in your project, showing which files are involved in each cycle.

2

Define an import hierarchy in .cursor/rules

Create rules that establish a clear dependency direction. This prevents Cursor from generating imports that flow in the wrong direction, which is the root cause of circular dependencies.

.cursor/rules/imports.mdc
1---
2description: Import hierarchy rules to prevent circular dependencies
3globs: "src/**/*.ts"
4alwaysApply: true
5---
6
7## Import Hierarchy (top imports from bottom, NEVER reverse)
81. src/routes/ imports from services, middleware
92. src/services/ imports from repositories, utils
103. src/repositories/ imports from models, utils
114. src/models/ imports from types only
125. src/middleware/ imports from services, utils
136. src/utils/ imports from types only, NO other internal imports
147. src/types/ ZERO internal imports (leaf module)
15
16## Rules
17- NEVER import from a higher layer (services must NOT import from routes)
18- NEVER create mutual imports between files in the same directory
19- Shared types go in src/types/, not in service or route files
20- If two modules need each other, extract the shared dependency into src/types/ or src/utils/
21- Use dependency injection instead of direct imports for cross-cutting concerns

Expected result: Cursor follows the import hierarchy and refuses to generate imports that violate the dependency direction.

3

Extract shared types to break cycles

The most common circular import pattern is two modules that share types. Fix this by extracting shared interfaces into a dedicated types file. Use Composer (Cmd+I) to automate the extraction.

Cursor Composer prompt
1// Composer prompt (Cmd+I):
2// @src/services/userService.ts @src/services/orderService.ts
3// These two files have circular imports because they both
4// define and use shared types. Extract all shared interfaces
5// and types into src/types/shared.ts. Update both files to
6// import from the new types file. Remove the circular import.
7
8// Before (circular):
9// userService.ts: import { Order } from './orderService'
10// orderService.ts: import { User } from './userService'
11
12// After (fixed):
13// types/shared.ts: export interface User {...} export interface Order {...}
14// userService.ts: import { User, Order } from '@/types/shared'
15// orderService.ts: import { User, Order } from '@/types/shared'

Pro tip: Move type/interface definitions to src/types/ as a general practice. This eliminates the most common source of circular imports.

Expected result: Shared types extracted to a leaf module, breaking the circular dependency chain.

4

Use dependency injection for runtime circular needs

When two services genuinely need to call each other at runtime, use dependency injection instead of direct imports. Ask Cursor to refactor the mutual dependency into constructor injection.

src/services/userService.ts
1// Cursor Chat prompt (Cmd+L):
2// @src/services/userService.ts @src/services/notificationService.ts
3// These services call each other: userService sends notifications,
4// notificationService looks up user preferences. Refactor to use
5// dependency injection. Pass the dependency through the constructor
6// instead of importing directly.
7
8// Expected pattern:
9class UserService {
10 constructor(private notificationService: INotificationService) {}
11
12 async deleteUser(id: string) {
13 await this.notificationService.send(id, 'Account deleted');
14 }
15}
16
17class NotificationService {
18 constructor(private userService: IUserService) {}
19
20 async send(userId: string, message: string) {
21 const prefs = await this.userService.getPreferences(userId);
22 // send based on prefs
23 }
24}

Expected result: Both services depend on interfaces instead of concrete implementations, eliminating the circular import.

5

Verify the fix with madge or Cursor re-audit

After restructuring, verify that all circular imports are resolved. Run madge again or ask Cursor to re-check the import graph.

Terminal
1// Terminal:
2npx madge --circular --extensions ts src/
3
4// Or Cursor Chat (Cmd+L):
5// @codebase Re-check for circular imports in src/.
6// Verify that the import hierarchy is respected:
7// routes -> services -> repositories -> models -> types.
8// Report any remaining violations.

Expected result: Zero circular imports detected. All imports follow the established hierarchy.

Complete working example

.cursor/rules/imports.mdc
1---
2description: Import rules to prevent circular dependencies
3globs: "src/**/*.{ts,tsx,js,jsx}"
4alwaysApply: true
5---
6
7## Module Dependency Hierarchy
8Imports flow DOWN this list only. Never import upward.
9
101. src/app/ or src/routes/ (entry points)
11 - May import: services, middleware, types
122. src/middleware/
13 - May import: services, utils, types
143. src/services/
15 - May import: repositories, utils, types
16 - NEVER import from: routes, middleware, other services (use DI)
174. src/repositories/
18 - May import: models, utils, types
19 - NEVER import from: services, routes
205. src/models/
21 - May import: types only
226. src/utils/
23 - May import: types only
24 - MUST be pure utilities with zero internal dependencies
257. src/types/
26 - ZERO internal imports (leaf node)
27 - Contains all shared interfaces, enums, and type aliases
28
29## Anti-patterns to AVOID
30- Two files importing from each other (A -> B -> A)
31- Service A importing Service B directly (use interfaces + DI)
32- Inline type definitions that create import dependencies
33- Re-exporting from barrel files that create hidden cycles
34
35## How to fix circular imports
361. Extract shared types to src/types/
372. Use dependency injection for runtime dependencies
383. Create an interface in src/types/ and depend on the interface
394. Use events/callbacks instead of direct method calls

Common mistakes when fixing broken imports from Cursor

Why it's a problem: Creating barrel index.ts files that re-export everything

How to avoid: Use specific file imports instead of barrel imports in services. Reserve barrel files for types/ and components/ only.

Why it's a problem: Defining types inline in service files instead of in types/

How to avoid: Move all shared interfaces to src/types/. Add a rule: 'Shared types go in src/types/, not in service files.'

Why it's a problem: Ignoring the error because the code 'works in development'

How to avoid: Treat circular import warnings as errors. Run npx madge --circular in your CI pipeline to catch them early.

Best practices

  • Define a clear module dependency hierarchy in .cursor/rules and enforce it in code reviews
  • Keep types/ as a leaf module with zero internal imports
  • Use dependency injection for cross-service communication instead of direct imports
  • Run npx madge --circular in your CI pipeline to catch circular imports automatically
  • Extract shared interfaces to dedicated type files rather than defining them in service files
  • Use specific file imports over barrel index.ts files in service layers
  • Start a new Cursor Chat session when working on import restructuring to avoid context pollution

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Node.js TypeScript project where userService.ts imports from orderService.ts and orderService.ts imports from userService.ts, creating a circular dependency. Both share User and Order types. Refactor this: extract shared types to a types file, use dependency injection for runtime dependencies, and ensure the import hierarchy follows routes -> services -> repositories -> types.

Cursor Prompt

In Cursor Chat (Cmd+L): @codebase @.cursor/rules/imports.mdc Find all circular import chains in src/. For each cycle, suggest a fix: extract shared types to src/types/, use dependency injection for runtime needs, or restructure the module hierarchy. Show the specific import changes for each fix.

Frequently asked questions

Why does Cursor create circular imports?

Cursor resolves imports based on type matching, not dependency graphs. If ServiceB has the type ServiceA needs, Cursor imports from ServiceB regardless of whether ServiceB already imports from ServiceA.

Will TypeScript catch circular imports at compile time?

TypeScript's compiler does not error on circular imports. It resolves them silently, which can cause undefined values at runtime. You need a tool like madge or a Cursor audit to detect them.

Are circular imports always bad?

In most cases, yes. They indicate unclear module boundaries and can cause subtle runtime bugs. Rare exceptions exist in tightly coupled modules that are always loaded together, but these should be merged into a single module instead.

How do I fix circular imports in React component files?

Extract shared component types to a types file. If two components render each other (mutual recursion), use React.lazy() for one of them or restructure the component hierarchy so the dependency flows in one direction.

Can Cursor automatically fix circular imports across my project?

Use Composer Agent mode (Cmd+I) with the prompt: 'Fix all circular imports in src/ by extracting shared types to src/types/ and using dependency injection for runtime dependencies. Process one circular chain at a time.' Review each fix before accepting.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.