Chat vs Composer in Cursor: when I use each

Table of Contents
Table of Contents
People ask how to use Cursor AI chat, then they paste a six-file feature into the sidebar and wonder why the diff is a mess.
Chat is for questions. Composer (the multi-file edit surface, Agent in some builds) is for changing files. I mix them up when I am tired. The PR pays for it.
I’m Aris Setiawan. Senior full-stack, first Cursor Ambassador in Indonesia. I ship client work in Cursor every week. This is not a feature tour. It is when I pick which surface, on a real repo.
The full production loop is here: How to use Cursor AI to ship faster without wrecking the codebase. The messy-repo version is How to use Cursor AI effectively on a real repo. This post is just the fork: chat or composer.
They are not the same tool

Chat lives in the sidebar. You ask. It reads what you @, plus whatever it needs. It writes an answer. Sometimes it offers an apply. I treat that apply as a suggestion, not a commit.
Composer (or Agent) is a job. You give it a scoped change. It opens files, edits several of them, and comes back with a diff you are supposed to read like a junior’s PR.
If I ask Chat to “add CSV export on invoices,” it will explain a plan, maybe dump a snippet that does not match this repo’s helper, and I will paste it into the wrong folder. If I give Composer the same sentence with no fence, it will touch billing, rename a shared type, and open a 30-file diff.
Same model. Different job. The surface is the contract.
When I use Chat
I use Chat when I do not know the shape yet.
Trace. Where does this invoice status actually get set? Which package owns the CSV helper? I @ one or two files and ask. I want a map, not a patch.
Plan. “We need export on the list. Propose the smallest change. Do not write files. Name the paths.” I make it argue with me in Chat until the plan fits on a sticky note.
Explain a weird error. Stack traces, a failing test, a type that only explodes in CI. Chat is faster than me grepping with one eye on Slack.
Review a small hunk. I paste a function and ask what looks off. I do not ask Chat to rewrite the module while I am still reading it.
A Chat prompt I actually use:
In
apps/web/app/invoices, where does list filtering live? Do not suggest new files. Point me at the existing helper if there is one.
If the answer names a folder I did not know about, I open that folder. Then I decide if Composer is allowed to touch it.
I do not use Chat to “just apply this across the app.” Apply-from-chat on a client repo is how a helper lands in utils2.ts next to the one we already have.
When I use Composer
I use Composer when I can name the files.
The change is clear. The blast radius is a sentence. I already looked at a sibling file so it can match the local pattern.
Multi-file, still small. A list page plus the export helper. A form plus the action. Not “refactor the billing package.”
PR-sized. A teammate should finish the diff in one sitting. If Composer wants a second package I did not name, I stop it.
A Composer prompt I actually use:
Add CSV export on the invoices list. Touch
apps/web/app/invoicesandpackages/exportonly. Do not touchpackages/billingor webhooks. Reuse the existing CSV helper. Match the error shape in the nearby list route.
Open the files first. @ those paths. Name what is off-limits. Same habit as the effective-on-a-real-repo post. Composer without a fence is Chat with write access. That is worse, not faster.
If I cannot name the folders, I am not ready for Composer. Back to Chat.
My loop

Unclear: Chat. Clear change: Composer. Then I still read it.
I do not skip the last box. Composer is confident. Confident is not correct. I read the diff like a senior reading a junior. Types, names, the extra file I did not ask for, the test it skipped because “the pattern was obvious.”
If the diff is bigger than the sticky note, I revert the extra and split the work. Two Composer jobs beat one “while we are here” job.
A loop I trust:
- Ask Chat where this kind of change lives.
- Open those files. Write the fence.
- Run Composer once.
- Read every file it touched. If it opened a stranger, stop.
That is how to use Cursor AI chat without letting it quietly become an unscoped agent.
Mistakes I still see
Pasting a spec into Chat and hitting apply. The spec was a wish. Apply is a commit-shaped object. Keep the wish in Chat. Keep the commit in Composer, with a fence.
Composer with the whole repo in context. It will “help” the neighboring app. Helpful without a map is how Friday’s PR becomes Monday’s revert.
Skipping review because the summary sounded right. Summaries are marketing. The diff is the work. I read the diff.
Using Composer to ask questions. You can. I do not. Questions are cheaper in Chat. Composer will start editing while it explains.
Switching models to fix a bad surface pick. A better model in the wrong surface still writes the wrong size of change. Pick the surface first.
What I tell mentees
If you are still installing Cursor, start with the beginner tutorial. If you already ship, remember this:
Chat is the senior in the room when you are lost. Composer is the junior you hired for a scoped ticket. You are still the one who merges.
I coach this in Cursor mentoring when a team keeps getting huge diffs. The fix is almost never “use a smarter model.” It is “stop asking Chat to ship, and stop asking Composer to think.”
If you want me on the product instead of the workflow, that is the Build path. Most people who ask me about chat vs Composer just need the loop above, on their actual repo, once.



