Problem: Switching Between Files Kills Your Flow
You're building a feature that needs changes across 5 files - components, API routes, database schema, types, and tests. Traditional AI code assistants make you copy-paste context between conversations or manually edit each file.
You'll learn:
- How Composer Mode edits multiple files simultaneously
- Managing AI context for complex refactors
- When to use Composer vs inline chat
- Real workflow for a full-stack feature
Time: 20 min | Level: Beginner
Why Composer Mode Exists
Cursor's inline chat (Cmd+K) works great for single-file changes. But real development crosses file boundaries - adding an API endpoint means updating routes, handlers, types, and tests.
Common symptoms without Composer:
- Switching between 6+ tabs to implement one feature
- AI loses context about previous changes
- Manual work connecting component → API → database
- Forgetting to update TypeScript types
What Composer fixes: Multi-file awareness. One conversation edits your entire feature stack.
Solution
Step 1: Open Composer Mode
# Keyboard shortcut
Cmd+I (Mac) / Ctrl+I (Windows/Linux)
# Or click the Composer icon in sidebar
Expected: A full-screen chat interface appears with file attachment options.
You should see:
- Chat input at bottom
- File picker on the right
- Recent files in dropdown
Step 2: Add Context Files
# In Composer, type @ to see file picker
@src/components/UserProfile.tsx
@src/api/users.ts
@prisma/schema.prisma
@src/types/user.ts
Why this matters: Composer reads these files before generating code. It understands how your components connect to APIs.
Pro tip: Add files in dependency order (schema → types → API → component) so AI sees the data flow.
Step 3: Describe Your Feature
Add a "last login" timestamp to user profiles.
1. Add lastLogin field to User model (DateTime)
2. Update API to save timestamp on login
3. Display "Last seen: X hours ago" in UserProfile component
4. Add TypeScript types
Why be specific: Vague prompts like "improve user feature" create guesswork. Numbered steps give Composer a clear plan.
If AI asks questions:
- "Which database?": Specify (Postgres, MySQL, SQLite)
- "Format for timestamp?": Give example ("2 hours ago" vs "Feb 8, 2:30 PM")
Step 4: Review Multi-File Changes
Composer shows a diff view with tabs for each modified file:
// prisma/schema.prisma
model User {
id String @id @default(cuid())
email String @unique
+ lastLogin DateTime?
}
// src/types/user.ts
export interface User {
id: string;
email: string;
+ lastLogin: Date | null;
}
// src/api/users.ts
export async function loginUser(email: string) {
return await db.user.update({
where: { email },
+ data: { lastLogin: new Date() }
});
}
Check these things:
- Types match between schema and TypeScript
- Database field is nullable (lastLogin?)
- API actually saves the data
- Component handles null state
Step 5: Apply or Iterate
# Accept all changes
Click "Accept All"
# OR refine specific files
"Change lastLogin to use UTC timezone in the API"
Why iterate: First pass might miss edge cases. Tell Composer what needs fixing instead of manual edits.
Common iterations:
- "Add error handling for database update"
- "Make the timestamp relative (2 hours ago) not absolute"
- "Add a test for loginUser function"
Step 6: Run Migration (for database changes)
# Generate Prisma migration
npx prisma migrate dev --name add-last-login
# Expected output
✓ Prisma schema loaded
✓ Migration generated: 20260208_add_last_login
✓ Database synchronized
If it fails:
- Error: "Field already exists": Check if previous migration wasn't applied
- Error: "Type mismatch": Database might be MySQL (DATETIME vs DateTime?)
Verification
Test the Full Stack
// Test in your app
// 1. Login as a user
await loginUser('test@example.com');
// 2. Check database
const user = await db.user.findUnique({
where: { email: 'test@example.com' }
});
console.log(user.lastLogin); // Should show current timestamp
// 3. View component
// Navigate to user profile - should show "Last seen: just now"
You should see:
- Database has populated lastLogin field
- Component renders relative time
- No TypeScript errors
Real-World Workflow Example
Adding Stripe Payment to SaaS App
Files to attach:
@prisma/schema.prisma # Add subscription model
@src/types/subscription.ts # TypeScript types
@src/api/stripe/webhook.ts # Webhook handler
@src/api/stripe/checkout.ts # Checkout session
@src/components/PricingTable.tsx # UI component
@.env.example # Document new env vars
Prompt:
Add Stripe subscription handling:
1. Create Subscription model (userId, stripeId, status, planType)
2. Implement webhook to update subscription status
3. Add checkout session endpoint
4. Update PricingTable to redirect to Stripe checkout
5. Add STRIPE_SECRET_KEY to .env.example
Use Stripe API v2024-02-15 with TypeScript SDK.
Why this works:
- Composer sees schema → knows to update types
- Sees webhook file → knows to verify signature
- Sees .env.example → documents new secret
- Single conversation handles 6 files
Composer vs Inline Chat (Cmd+K)
Use Composer When:
- Feature spans 3+ files
- Refactoring shared utilities
- Adding database migrations
- Need to maintain context across changes
Use Inline Chat (Cmd+K) When:
- Quick fix in current file
- Writing a single function
- Explaining code block
- Generating test for visible function
Example: Renaming a prop across 8 components? Composer. Fixing a typo? Cmd+K.
Advanced Tips
1. Use Composer Rules (Custom Instructions)
Create .cursorrules in project root:
# .cursorrules
- Always use Zod for validation in API routes
- Prefer server actions over API routes in Next.js 14+
- Use Tailwind classes, no custom CSS
- Add JSDoc comments to exported functions
Why: Composer follows these rules automatically. No need to repeat "use Tailwind" in every prompt.
2. Reference Documentation
# In Composer
"Add rate limiting using @upstash/ratelimit docs"
Composer can search docs if you mention the package. More accurate than generic "add rate limiting."
3. Incremental Changes
# Instead of:
"Build entire authentication system"
# Do:
1. "Add user registration endpoint"
[Review, accept]
2. "Add login with JWT tokens"
[Review, accept]
3. "Add protected route middleware"
[Review, accept]
Why: Smaller changes are easier to review. You catch issues before they cascade.
4. Context Management
Too much context (10+ files) → AI gets confused, slower responses
Too little context → AI invents code that doesn't match your patterns
Sweet spot: 3-6 related files
Example for user profile feature:
- ✅ Schema, types, API, component (4 files)
- ❌ Entire /src folder (80+ files)
Common Mistakes
❌ Not Specifying Tech Stack
Bad prompt:
"Add authentication"
AI doesn't know: Next.js App Router? Pages Router? Express? Auth0? Clerk? Custom JWT?
Good prompt:
"Add email/password auth using NextAuth v5 with Prisma adapter and PostgreSQL"
❌ Accepting Without Reading Diffs
Why it fails: AI might:
- Remove error handling you had
- Change function signatures breaking other code
- Use deprecated APIs
Always review:
- Lines removed (in red)
- Import changes
- Function signature changes
❌ Forgetting About Tests
# After accepting feature changes, add:
"Create a test file for the loginUser function using Vitest"
Composer can generate tests that actually match your implementation.
What You Learned
- Composer Mode edits 2-10 files in one conversation
- Attach files with
@to give AI context about your codebase - Review diffs before accepting - AI isn't perfect
- Use
.cursorrulesfor project-specific patterns - Incremental changes > giant refactors
Limitations:
- AI can hallucinate APIs that don't exist - verify imports
- Complex refactors (20+ files) still need manual oversight
- Generated tests might miss edge cases
Troubleshooting
Issue: Composer Changes Wrong Files
Symptom: Asked to update UserProfile component, but it changed AdminProfile instead
Fix:
# Be explicit with file paths
"Update ONLY src/components/user/UserProfile.tsx to show last login"
Issue: "Model context length exceeded"
Symptom: Error when attaching many files
Fix:
- Detach large files (package-lock.json, dist/, node_modules)
- Break into smaller tasks
- Use Composer for planning, Cmd+K for implementation
Issue: AI Uses Outdated Syntax
Symptom: Generated React class components instead of hooks
Fix:
# Add to .cursorrules
- Use React 18+ functional components with hooks
- Prefer modern async/await over .then() chains
Keyboard Shortcuts Reference
| Action | Mac | Windows/Linux |
|---|---|---|
| Open Composer | Cmd+I | Ctrl+I |
| Inline Chat | Cmd+K | Ctrl+K |
| Accept Change | Cmd+Enter | Ctrl+Enter |
| Reject Change | Cmd+Backspace | Ctrl+Backspace |
| Attach File | @filename | @filename |
Comparing AI Code Editors (Feb 2026)
| Feature | Cursor Composer | GitHub Copilot Chat | OpenClaw |
|---|---|---|---|
| Multi-file edit | ✅ Native | ⚠️ Limited | ✅ Via plugins |
| Context files | Up to 10 | 3-5 | Unlimited (local) |
| Codebase search | ✅ Built-in | ❌ Manual | ✅ Open-source |
| Local models | ❌ Cloud only | ❌ Cloud only | ✅ Ollama/LM Studio |
| Price | $20/mo | $10/mo | Free (self-hosted) |
OpenClaw advantage: Run Llama 3.3 70B locally with same Composer-like UX. No code leaves your machine.
Tested on Cursor v0.55.2, macOS Sonoma, Next.js 15.1.4, TypeScript 5.5.4