JavaScript Formatting Wars: Tabs vs Spaces and Beyond
Code formatting debates are as old as programming itself. Tabs vs spaces, semicolons vs no semicolons, trailing commas or not. The truth is that the specific choice matters far less than consistency. Here is a practical guide to ending the formatting wars on your team.
Why Formatting Matters
Inconsistent formatting creates noisy diffs. When one developer uses tabs and another uses spaces, every touched line shows as changed in version control, hiding the actual logic changes. Code review becomes tedious, merge conflicts multiply, and readability suffers.
Automated formatting eliminates this entirely. Every file follows the same rules, every diff shows only meaningful changes, and no one spends time arguing about style in code reviews.
The Big Decisions
Tabs vs Spaces
Tabs let each developer set their preferred visual width. A tab might render as 2 spaces for one person and 4 for another. This is an accessibility advantage for developers who need larger indentation.
Spaces guarantee identical rendering everywhere: in the editor, on GitHub, in terminal output, and in code snippets. Most JavaScript projects have standardized on 2 spaces.
The JavaScript ecosystem overwhelmingly uses spaces. Both Prettier and the Google/Airbnb style guides default to 2-space indentation.
Semicolons
JavaScript's Automatic Semicolon Insertion (ASI) means semicolons are technically optional in most cases. But ASI has edge cases that can cause bugs:
// This breaks without a semicolon
const getValue = () => 42
[1, 2, 3].forEach(console.log)
// Interpreted as: const getValue = () => 42[1, 2, 3].forEach(...)
Most teams opt for mandatory semicolons to avoid these pitfalls. Some (notably the Standard JS style) prefer no semicolons with explicit rules about starting lines with [, (, or backticks.
Trailing Commas
Trailing commas in multiline structures produce cleaner diffs:
// Without trailing comma: adding "d" changes two lines
const items = [
"a",
"b",
- "c"
+ "c",
+ "d"
];
// With trailing comma: adding "d" changes one line
const items = [
"a",
"b",
"c",
+ "d",
];
ES2017+ allows trailing commas in function parameters and arguments. Prettier defaults to "all" for trailing commas.
Configuring Prettier
Prettier is the dominant JavaScript formatter. It is opinionated by design, offering few configuration options to minimize debates. A typical .prettierrc file:
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 80,
"arrowParens": "always",
"endOfLine": "lf"
}
Run it on your entire codebase:
npx prettier --write "src/**/*.{js,ts,tsx,json,css}"
Integrate with your editor to format on save, and add a pre-commit hook to catch anything that slips through.
ESLint for Style Rules
While Prettier handles formatting (whitespace, line breaks, parentheses), ESLint handles code quality (unused variables, implicit globals, complexity). The two complement each other.
Use eslint-config-prettier to disable ESLint rules that conflict with Prettier:
npm install -D eslint-config-prettier
// .eslintrc.json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
]
}
This setup gives you Prettier for formatting and ESLint for catching bugs, with no conflicts between them.
EditorConfig for Cross-Editor Consistency
Not everyone uses the same editor. An .editorconfig file sets basic formatting rules that most editors respect:
# .editorconfig
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
This covers the basics even for team members who have not configured Prettier in their editor.
Pre-Commit Hooks with Husky
Enforce formatting before code reaches the repository:
npm install -D husky lint-staged
npx husky init
// package.json
{
"lint-staged": {
"*.{js,ts,tsx}": ["prettier --write", "eslint --fix"],
"*.{json,css,md}": ["prettier --write"]
}
}
This runs Prettier and ESLint only on staged files, keeping the hook fast even in large repositories.
Formatting Minified Code
Working with minified JavaScript is a common debugging task. A beautifier can transform a single-line bundle into readable, indented code:
// Before: minified
function a(b,c){if(b>c){return b-c}else{return c-b}}
// After: beautified
function a(b, c) {
if (b > c) {
return b - c;
} else {
return c - b;
}
}
This does not restore variable names, but it makes the control flow visible. Combined with source maps, you can trace minified code back to its original form.
Team Adoption Checklist
- Choose and commit a
.prettierrcconfiguration. - Add
.editorconfigfor cross-editor basics. - Run
prettier --writeon the entire codebase in a single commit. - Set up
lint-stagedwith a pre-commit hook. - Configure CI to run
prettier --checkto catch regressions. - Document the setup in your contributing guide.
The initial formatting commit will touch many files. Do it early in the project or as a standalone PR that reviewers can approve without line-by-line review.
Try our JS Beautifier to format and beautify JavaScript code instantly — right in your browser, no upload required.