Skip to content

Cursor Tab: get multi-line edits right and verify them

Cursor Tab is the editor’s built-in completion model: it predicts the next edit near the cursor, including multi-line changes and jumps to the next place that needs a related change, and the Tab key accepts it. Tab is most reliable when the pattern to continue is already visible, and accepted code still needs a type, lint or test gate.

You press Tab, get a plausible 12-line handler, and accept it. Two days later a reviewer finds that it returns "User not found" from the posts endpoint, because Tab copied the pattern faithfully and the one detail that mattered came along with it. Speed was never the problem; knowing which suggestions to trust, and catching the ones you should not have trusted, is.

This page is for developers who still type code in Cursor’s editor and want Tab to carry the mechanical part of it. Feature-sized work belongs to Cursor’s agent; Tab is for the edits you would otherwise type yourself.

What you’ll walk away with from these Tab tips

Section titled “What you’ll walk away with from these Tab tips”
  • A working model of what Tab predicts, and what that means for where you put your cursor and which files you edit first.
  • A two-example seeding technique and an intent-comment recipe that make the first suggestion the right one more often.
  • Three copy-paste prompts that hand off from Tab to inline edit and Agent: one to rewrite a scaffold, one to check a Tab-written batch, one to prove Tab-written tests can fail.
  • A set of cheap gates (exhaustiveness checks, satisfies, watch-mode tests) that catch a wrong suggestion without you rereading every accepted line.
  • A failure-mode table for the ways Tab goes wrong, with the recovery for each.

How does Cursor Tab decide what to suggest?

Section titled “How does Cursor Tab decide what to suggest?”

Tab works from what is near the cursor and from your last edit. The subsections below explain what it predicts, how to put the pattern in its view, and which jobs it suits.

Tab predicts the next edit, not the next word

Section titled “Tab predicts the next edit, not the next word”

A classic autocomplete finishes the token you are typing. Cursor Tab proposes an edit: it can rewrite several lines around the cursor, and after you accept one change it can offer a jump to the next location that needs a related change. In practice that means one accepted suggestion is often the first of a short sequence:

  • You rename a parameter; Tab offers the same rename at the next usage.
  • You add a field to one object literal; Tab offers it in the sibling literal below.
  • You add a call to a new helper; Tab offers the import it needs.

Treat each Tab press as accepting one step of a sequence, and check each step as you go. A chain of ten accepted edits is ten decisions, not one.

Tab predicts from the code around the cursor and from the edit you made last. The practical rule follows: make the pattern visible before you ask Tab to continue it. Before you write a new endpoint, make sure the closest existing one sits in the same file, ideally directly above where the new code goes, or make the first change by hand so Tab has an edit to continue. A suggestion that ignores your conventions usually means the example it needed was not near the cursor.

For deliberate context selection with the agent (@-mentions, rules, indexing), see context management in Cursor. Tab has no prompt box for @-mentions (Cursor 3.x line, as of 2026-09-26); you steer it with the code you place near the cursor.

Use Tab for more of the same, not for something new

Section titled “Use Tab for more of the same, not for something new”

Tab’s strength is continuation. When the next edit is a variation of something already on screen, it is the fastest surface Cursor has. When the next edit needs a decision that is not visible anywhere, use a surface that takes an instruction.

The next edit is…UseWhy
The fourth instance of a pattern with three on screenTabThe pattern is visible, so the suggestion is mostly right
A change to one selected block (“add retry with backoff”)Inline editYou need to state an instruction the code does not contain
A change across files, or one that needs running testsAgentIt can read, edit and run commands, then report evidence
Business logic nobody has written yetAgent with a spec, or youTab can only guess at rules it has never seen

Accept, partially accept or reject a Tab suggestion

Section titled “Accept, partially accept or reject a Tab suggestion”

Every suggestion is a choice to take all of it, part of it or none of it. The subsections below cover the keys, what to check before you accept, and how to chain accepted edits through a repetitive change.

Take only the part of a suggestion that is right

Section titled “Take only the part of a suggestion that is right”

You do not have to take a suggestion whole:

  • Tab accepts the whole suggestion.
  • Partial accept takes it word by word. The last reported default is Cmd+Right Arrow on macOS (Ctrl+Right Arrow on Windows and Linux) as of 2026-09-26; confirm it by searching the Keyboard Shortcuts editor for “partial”, and see the keyboard shortcuts reference.
  • Escape dismisses the suggestion.

Partial accept pays off when the structure is right and a value is wrong: take the call and the first arguments, then type the argument Tab got wrong.

Judge each suggestion by the detail most likely to be wrong

Section titled “Judge each suggestion by the detail most likely to be wrong”

A suggestion that copies a pattern is rarely wrong in its shape. It is wrong in the one place the new case differs: the entity name, the status code, the error message, the fixture ID. Look there first, decide, and move on: accept with Tab or dismiss with Escape. Typing over a half-accepted suggestion leaves you with a merge of your intent and Tab’s, which is the hardest version to check later.

For a change that repeats down a file (a new field on every fixture, a renamed option in every call), let Tab walk it:

  1. Make the first change by hand, exactly as you want every instance to look.
  2. Press Tab when the next suggestion appears, or when Tab offers a jump to the next location.
  3. Check the one detail that varies per instance before each press.
  4. Stop the chain the moment a suggestion is wrong; fix that instance by hand, and Tab continues from your corrected edit.
  5. Run the file’s tests or the type checker once the chain ends.

Three habits make Tab’s first suggestion the one you want: examples, an intent comment and descriptive names.

Write two examples, let Tab write the third

Section titled “Write two examples, let Tab write the third”

The most dependable Tab workflow: write two instances of a pattern by hand, then put the cursor where the third starts. Two examples show Tab both what stays constant and what varies.

// Written by hand:
it("returns 200 for an existing user", async () => {
const res = await request(app).get("/api/users/1");
expect(res.status).toBe(200);
expect(res.body).toHaveProperty("id");
});
it("returns 404 for a missing user", async () => {
const res = await request(app).get("/api/users/999");
expect(res.status).toBe(404);
expect(res.body.error).toBe("User not found");
});
// Type `it(` here. Tab typically proposes the next case in the same shape,
// for example a 400 for a non-numeric ID. Check the status and the message.

Write the intent as a comment before the code

Section titled “Write the intent as a comment before the code”

A comment above an empty line gives Tab the one thing the surrounding code cannot: what you intend to do next. The comment works best when it names both the condition and the exact response.

Delete or keep the comment afterwards on its merits; a comment that restates the next line adds nothing to the file.

Tab reads your identifiers. const data = await fetch(url) tells it nothing about what comes next; const userProfileResponse = await fetch(`/api/users/${userId}/profile`) tells it the resource, the ID and that the next lines probably check the response and parse a profile. Descriptive names cost nothing and remove guesswork from every suggestion that follows.

Where Tab does its best work, and the gate that checks it

Section titled “Where Tab does its best work, and the gate that checks it”

Each of the patterns below is one where Tab is usually right, paired with a check that catches it when it is not. The gate matters more than the pattern: it is what lets you accept quickly.

Switch statements, checked for exhaustiveness

Section titled “Switch statements, checked for exhaustiveness”

Write the first case by hand and Tab proposes the rest from the union type. Add an exhaustiveness check so a missing or misspelled case is a compile error instead of something you have to spot:

type Status = "pending" | "active" | "suspended" | "deleted";
function statusLabel(status: Status): string {
switch (status) {
case "pending":
return "Pending review";
// Tab proposes "active", "suspended" and "deleted" in the same shape.
default: {
const unhandled: never = status;
throw new Error(`Unhandled status: ${unhandled}`);
}
}
}

If Tab skips a member of the union, status is not never in the default branch and the type checker fails. Adding a new status later fails the build in the same way.

Configuration objects, checked with satisfies

Section titled “Configuration objects, checked with satisfies”

Tab fills configuration objects well once it has seen two or three properties. It also fills them with plausible values that are not yours. Anchor the object to its type so an invented or misspelled key fails:

const dbConfig = {
host: "localhost",
port: 5432,
// Tab proposes database, user, pool size and so on from the type and nearby code.
} satisfies DatabaseConfig;

satisfies rejects a property the type does not declare, and a missing required one, while keeping the literal types. What it cannot catch is a wrong value of the right type, such as a pool size copied from another service. Environment-dependent values belong in environment variables, not in whatever Tab saw last.

With the implementation open in the editor, start a describe block in the test file, write one case by hand and let Tab propose the next ones. Tab-written tests have a specific failure mode: they can assert what the code does rather than what it should do, so they pass against a bug. Prove they can fail before you trust them.

A mutation that no test catches is a test you still need to write, and that is a better use of your attention than rereading the tests Tab wrote. For the full test-first loop in Cursor, see test-driven development in Cursor.

Hand off from Tab to inline edit and Agent

Section titled “Hand off from Tab to inline edit and Agent”

Tab stops being the right tool once a change needs a rule the code does not contain, a check across many accepted lines, or no suggestions at all. The subsections below cover each handoff.

Scaffold with Tab, then rewrite the core with inline edit

Section titled “Scaffold with Tab, then rewrite the core with inline edit”

Tab is fast at the parts that repeat: signature, validation shape, error handling, return type. It cannot know a business rule that appears nowhere in the code. Let Tab produce the scaffold, then select the body and state the rule with inline edit (Cmd+K on macOS, Ctrl+K on Windows and Linux by default; confirm in the keyboard shortcuts reference).

After the inline edit lands, Tab picks up again from the new code: it tends to offer the matching test cases or call-site changes next, and the same gates apply to those.

Check a Tab-written batch without reading every line

Section titled “Check a Tab-written batch without reading every line”

Accepting Tab suggestions for an hour produces a diff in which every line looked plausible at the moment you accepted it. Rereading all of it is the slow path. Use gates for the structure and point your attention, or an agent’s, at the details copied patterns get wrong.

  1. Keep the type checker and the test runner in watch mode while you work (npx tsc --noEmit --watch and npx vitest, or your stack’s equivalents). A wrong Tab suggestion that breaks a type or a test shows up seconds after you accept it, not in CI.
  2. Let lint run on save. Unused imports, shadowed variables and unreachable code are typical leftovers of a chain that went one step too far.
  3. Before you commit, ask Agent for a targeted check of the details that pattern-copying gets wrong, using the prompt below. It reads the diff so you read only its findings.
  4. You sign off at the commit. Tab never commits, and the accept key is not a review. The commit is where you decide, on the evidence from steps 1–3, that the batch is yours.

For the wider practice of trusting evidence over line-by-line reading, see reading evidence instead of code.

Turn Tab off where it costs more than it saves

Section titled “Turn Tab off where it costs more than it saves”

Tab suggestions in prose, large data files or a hand-written algorithm you are reasoning through can cost more attention than they save. Cursor Tab is Cursor’s own model, not VS Code’s IntelliSense, so it is configured in Cursor Settings, not through VS Code’s editor.suggest.* settings.

In earlier 3.x builds the Cursor Tab status-bar item offered snooze and per-extension disable (for file types such as md or json), and Cursor Settings had a Tab section. These controls were not re-checked on 2026-09-26, so search Cursor Settings for “Tab” to find them in your build.

If your build has them, snooze suits a focused hour of non-code work, and a per-extension disable suits a file type that is consistently noisy.

SymptomLikely causeRecovery
The suggestion has the right shape and a wrong value (status code, message, name)Tab copied the nearest example, including the detail that should have changedPartial-accept the structure and type the value; run the diff-review prompt above before committing
Tab keeps proposing the old pattern after a migration (class components, a deprecated client)The old pattern is what sits near the cursorMigrate one or two instances by hand first, in the same file or directly above; Tab continues from recent edits
Tab imports a symbol from the wrong packageSeveral packages export the same nameDismiss, write the correct import once at the top of the file, then continue; the type checker catches the wrong one if you accepted it
Tab-written tests all pass, including against a known bugThe tests assert current behaviour, not intended behaviourRun the mutation prompt above; add the cases it shows are missing
Tab proposes a real-looking URL, key or hostnameIt continued a literal it saw elsewhere in the fileDismiss it; read the value from configuration or an environment variable. Search the diff for literals before you commit
The Tab key does nothing, or inserts a different completionAnother completion extension, such as GitHub Copilot, is bound to the same key, or Tab is snoozedCheck whether Tab is snoozed or disabled (search Cursor Settings for “Tab”); disable the other extension’s inline suggestions for this workspace
A long chain drifted and you are not sure which edits were rightSeveral accepted steps compounded one early mistakeStop, run the type checker and tests, and undo back to the last green state instead of patching forward