tabWidth β how wide one indentation level is, in columns
The Prettier Tab Width option
tabWidth sets how many columns one level of indentation occupies. With the default useTabs: false it is literally the number of spaces Prettier emits per level; with tabs enabled it becomes the width a tab is assumed to render as, which is what printWidth measures against.
Number of spaces per indentation level.- Default
2- Type
int- CLI flag
--tab-width
What the option does
The default is 2. Every nesting level β a function body, an object literal, a JSX child β is indented by one unit of tabWidth.
The subtlety is that this option has two different jobs depending on useTabs. With spaces, it decides what Prettier writes: tabWidth: 4 emits four spaces. With tabs, Prettier writes one tab character regardless, and tabWidth becomes an assumption about how wide that tab will look β used purely so that line-width measurement is accurate.
That second role matters more than it sounds, because indentation counts toward printWidth. A deeply nested line at tabWidth: 4 has half the remaining budget of the same line at tabWidth: 2, so raising this option quietly makes your code wrap sooner.
Why the default is 2
Two spaces is the prevailing convention in JavaScript and has been since well before Prettier existed β it is what Node, React, Vue and the majority of npm packages use. Prettier's defaults generally follow the ecosystem rather than trying to correct it.
There is a structural argument too. JavaScript nests deeply: a callback inside a method inside a class inside a module is four levels before any real code appears. At four spaces per level that is sixteen columns of the eighty available, spent on whitespace. Two keeps more of the line for content.
When to change it
- Match the surrounding ecosystem when your project is not primarily JavaScript. Python projects that also contain JS often standardise on
4; PHP and C# codebases usually do too. - Raise it to
4if your team finds two-space indentation hard to scan. This is a genuine accessibility consideration for some readers, not merely taste. - Consider
useTabs: trueinstead of raising it. Tabs let each reader choose their own width, which serves both preferences at once β see below. - Leave it alone if you have no specific reason. It is the option with the largest reformat cost relative to the benefit of changing it.
Its relationship with .editorconfig
Prettier reads .editorconfig when one is present and no explicit Prettier setting overrides it. indent_size maps to tabWidth and indent_style maps to useTabs, which means an .editorconfig you have forgotten about can silently determine your formatting.
root = true
[*]
indent_style = space
indent_size = 2If your formatting does not match your .prettierrc, this file is the first place to look. An explicit value in the Prettier config always wins; the ambiguity only exists when the Prettier config is silent.
Common mistakes
- Expecting
tabWidthto change the character emitted. It only changes the count, unlessuseTabsis on β then it changes nothing that is written to disk. - Setting it without noticing the knock-on effect on
printWidth. Wider indentation means earlier wrapping. - Fighting an
.editorconfigthat is quietly supplying the value. - Changing it alongside real work, which makes the diff unreviewable β do it in its own commit and add that commit to
.git-blame-ignore-revs.
Use it in .prettierrc
Drop tabWidth into your Prettier config file:
{
"tabWidth": 2
}Try it in the generatorWorked examples
The same code formatted with each value of tabWidth.
tabWidth: 2
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(", ");
}tabWidth: 4
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
- Does tabWidth do anything when useTabs is true?
- Yes, but nothing that reaches the file. Prettier writes one tab per level either way;
tabWidthtells it how many columns to assume that tab occupies so line-width measurement againstprintWidthis correct. - Why is my indentation not what I configured?
- Most often an
.editorconfigfile is supplyingindent_sizeand your Prettier config is silent ontabWidth. An explicit Prettier setting takes precedence. - Should I use 2 or 4?
- Two matches the JavaScript ecosystem and leaves more of each line for code. Four is easier for some readers to scan and matches other language communities. If the team is split,
useTabs: truelets each reader pick their own.
Other Global options
Generated from Prettier 3.9.6.