JSFormat JS Formatter

Automatic Semicolon Insertion: The Lines That Join and the Return That Ends

JSFormat Guides · Updated 2026-10-31 · All guides

JavaScript inserts a semicolon at a line break only when the next token cannot continue the statement. That makes semicolon-free code safe almost everywhere and dangerous in a few specific places. The ASI checker finds them.

Lines that continue the previous one

A line that starts withJoins the previous line asExample
(a callconst a = b then (f)() means b(f)()
[an indexconst a = b then [1, 2].map(f) means b[1, 2].map(f)
a template literala tagged templatex then ` y means xy `
+ or -a binary operatora = b then -c means a = b - c
/a divisiona = b then /re/.test(s) means b / re / .test(s)

If the previous line ends in a value (a name, a string, ) or ]) and has no semicolon, the two lines are one statement. Defensive style puts a ; before such a line or ends every statement with one.

Restricted productions

return, break, continue and yield end at a line break. This returns undefined, and the object on the next line is a separate, unreachable block:

function f() {
  return
    { ok: true }
}

Put the value on the same line as return. The checker flags a return/break/continue/yield followed by a line break and a token.

After a closing brace

A line starting with (, [ or a template right after } is only a problem when that brace ended a function expression or object literal rather than a block. A tokenizer cannot tell which, so the checker reports it as "maybe".

What the checker does not do

It is a token-level scan, not a parser. It can miss a hazard, and a clean result means none of these patterns were found. Pair it with a linter rule (no-unexpected-multiline) in CI.

Developer Sponsor / Partner
Copied to clipboard!