semi β semicolons, and the ASI hazards behind the default
The Prettier Semi option
semi decides whether Prettier ends statements with a semicolon. The default is true, and the reason is not aesthetic: JavaScript's automatic semicolon insertion has a small number of genuinely surprising failure cases, and printing semicolons removes them from consideration entirely.
Print semicolons.- Default
true- Type
boolean- CLI flag
--no-semi
What the option does
With semi: true β the default β Prettier terminates every statement that can take a semicolon. With semi: false it omits them, except where leaving one out would change what the program means.
That exception is the whole story. JavaScript does not actually require semicolons, because the parser inserts them for you under a rule called automatic semicolon insertion, or ASI. ASI works by noticing that the next token cannot continue the current statement and closing the statement off. It is right almost always, and wrong in a handful of cases that bite hard.
The classic failure is a line that begins with a bracket or a parenthesis. The parser reads it as a continuation of the previous line β an index access, or a function call β rather than as a new statement:
const value = compute()
[1, 2, 3].forEach(run)
// parsed as:
const value = compute()[1, 2, 3].forEach(run)value.What Prettier does when you turn semicolons off
semi: false is safe in Prettier in a way that hand-written semicolon-free code is not. When a line would otherwise start with a character that continues the previous statement, Prettier prefixes it with a defensive semicolon:
const value = compute()
;[1, 2, 3].forEach(run)The characters that trigger this are [, (, a backtick, +, -, /, and a couple of rarer ones. You will see those leading semicolons in any semicolon-free codebase that uses Prettier. They look odd at first and they are not optional β removing one by hand reintroduces exactly the bug the style was supposed to avoid.
Why the default is true
Prettier's defaults lean towards the option that needs the least explanation to a newcomer, and semicolons win that test. A reader who has never heard of ASI can read semicolon-terminated code correctly; the reverse is not true, because the leading-semicolon idiom is genuinely confusing until someone explains it.
There is also a smaller, practical argument. Semicolons make each statement self-delimiting, so a line moved, duplicated or reordered by an editor or a merge stays valid. Without them, moving a line can silently join it to its new neighbour.
When to choose false
The case for semi: false is real. Semicolons carry no information a formatter cannot supply, and a codebase without them is measurably less dense on screen. Several large ecosystems β the standard style, much of the Vue and Nuxt world β settled on omitting them, so a project in that orbit will feel more at home matching its neighbours.
- Choose
falseif your team already writes this way, or your framework community does. Consistency with the code your contributors read elsewhere is worth more than the marginal safety. - Choose
trueif you have contributors at mixed experience levels, or if the codebase is long-lived and you would rather not explain leading semicolons in review. - Do not choose based on typing effort. Prettier writes them; you never type one.
ESLint, TypeScript and CI
ESLint has both a semi rule and a no-unexpected-multiline rule, and the first will fight Prettier if both are active. Disable the stylistic rules with eslint-config-prettier, placed last in your config, and let Prettier own the decision.
no-unexpected-multiline is the interesting one and worth keeping if it survives your config: it catches the ASI hazards above at lint time rather than at runtime. It is not a formatting rule, so it does not conflict.
TypeScript is unaffected by this setting β semicolons are optional there for the same reasons and with the same hazards. Class property declarations are the one place where the emitted punctuation differs slightly between the two settings, and Prettier handles that for you.
Common mistakes
- Deleting the leading semicolons Prettier inserts under
semi: false. They are load-bearing. - Leaving ESLintβs
semirule enabled alongside Prettier, which produces an unfixable loop where each tool undoes the other. - Flipping the setting on a mature codebase without a dedicated reformat commit β it touches nearly every line in the project.
- Assuming
semi: falsemeans no semicolons appear anywhere.forloops still need theirs; they are syntax, not statement terminators.
Use it in .prettierrc
Drop semi into your Prettier config file:
{
"semi": true
}Try it in the generatorWorked examples
The same code formatted with each value of semi.
semi: true
const user = {
name: "Ada",
"user-id": 7,
roles: ["admin", "editor"],
active: true,
};
const greet = (name) => `Hello ${name}`;
const label = user.active
? "active member of the team"
: "inactive member of the team";
export function summarize(items) {
return items
.filter((i) => i.active)
.map((i) => i.name)
.join(", ");
}semi: false
const user = {
name: "Ada",
"user-id": 7,
roles: ["admin", "editor"],
active: true,
}
const greet = (name) => `Hello ${name}`
const label = user.active
? "active member of the team"
: "inactive member of the team"
export function summarize(items) {
return items
.filter((i) => i.active)
.map((i) => i.name)
.join(", ")
}Common questions
- Is semi: false actually dangerous?
- Not when Prettier is the thing writing your code. Prettier inserts a defensive leading semicolon on any line that would otherwise be misparsed. The danger is in hand-written semicolon-free code, or in editing Prettier output by hand and removing one of those leading semicolons.
- Why does my file start a line with a semicolon?
- Because that line begins with
[,(, a backtick or an operator, and without the semicolon JavaScript would join it to the previous statement. Prettier added it deliberately undersemi: false. - Does this option affect TypeScript, JSON or CSS?
- It applies to JavaScript and TypeScript. JSON has no statements and is unaffected. CSS declarations always end in a semicolon as a matter of syntax, so the option does not apply there either.
Other JavaScript options
Generated from Prettier 3.9.6.