Minified bundles, one-liner arrow functions, a config file someone hand-edited at 2am — sooner or later you end up staring at JavaScript that's technically valid but nearly unreadable. Formatting is the boring fix that makes the rest of your work faster. Here's how I think about it after years of untangling other people's code (and, honestly, my own).
What formatting JavaScript actually means
Formatting is purely about presentation: indentation, line breaks, spacing around operators, quote style, semicolons, and where braces land. It doesn't change behaviour. A formatter parses your code into an abstract syntax tree and re-prints it according to a consistent set of rules. That's the key distinction — a proper formatter rebuilds the output from the structure, so it can't accidentally break logic the way a naive find-and-replace would.
Don't confuse formatting with linting. A linter (like ESLint) flags problems: unused variables, potential bugs, risky patterns. A formatter just makes the code look consistent. They complement each other, and modern setups usually run both.
When you'd reach for it
- Reading minified or bundled code. You pulled something from a CDN or a production build and need to understand what it does. Beautifying it back into readable lines is the fastest way in.
- Cleaning up copy-pasted snippets. Code from Stack Overflow, a tutorial, or a Slack message often arrives with mangled indentation.
- Onboarding a legacy codebase. When formatting is all over the place, a one-time pass makes diffs meaningful again.
- Enforcing consistency in a team. Nobody should argue about tabs versus spaces in code review. Let a tool decide once and move on.
For quick, one-off jobs — like inspecting a stray script or tidying a snippet before you paste it somewhere — an in-browser tool is the least-friction option. I keep this JavaScript Beautifier bookmarked for exactly that: drop code in, get readable output, done. No install, no config.
Common gotchas
Formatting a minified file loses semantic names
Beautifying restores line breaks and indentation, but it can't recover variable names that a minifier renamed to a, b, or t. You'll get structure back, not intent. That's still hugely useful for debugging, just don't expect miracles.
Automatic semicolon insertion still bites
Formatters follow your semicolon preference, but they won't save you from JavaScript's ASI rules. A classic trap:
function bad() {
return
{ ok: true }
}
// returns undefined, not the object
The engine inserts a semicolon after return, so the object literal is dead code. Formatting the file won't fix this — it's a logic bug. Watch for it whenever a return value mysteriously becomes undefined.
Comments can move in surprising ways
Because formatters work on the AST, comments sometimes get reattached to a nearby node. A comment above a line might end up beside it, or a trailing comment could shift. Usually harmless, occasionally confusing. Skim the diff.
Long lines and print width
Most formatters wrap at a target width (80 or 100 columns is common). That can turn a tidy chained call into a multi-line stack. It's correct, just be aware your line count will change and don't panic about the diff size.
Mixed quotes and template literals
If you normalise to single quotes, strings containing apostrophes get escaped or switched automatically. Fine, but review generated regex and strings where escaping matters.
A short worked example
Here's a compressed one-liner — the kind of thing you might find in a config or a hastily written handler:
const users=[{name:'ada',active:true},{name:'linus',active:false}];function activeNames(list){return list.filter(u=>u.active).map(u=>u.name)}console.log(activeNames(users));Technically fine, unreadable in practice. Run it through a beautifier and you get something you can actually reason about:
const users = [
{ name: 'ada', active: true },
{ name: 'linus', active: false },
];
function activeNames(list) {
return list.filter((u) => u.active).map((u) => u.name);
}
console.log(activeNames(users));Same behaviour, but now the array structure, the filter/map pipeline, and the function boundaries are obvious at a glance. If you were debugging why activeNames returned the wrong thing, the second form is where you'd spot it.
Making it stick in a project
One-off beautifying is great for exploration. For a real codebase, wire formatting into the workflow so it happens automatically:
- Format on save. Configure your editor to run the formatter whenever you save. Most people never think about formatting again after this.
- Commit hooks. A pre-commit hook that formats staged files keeps the repo clean without relying on everyone's editor being set up correctly.
- CI check. A CI step that fails if code isn't formatted stops style drift from creeping back in. Keep the message friendly — it should point people at the fix command.
Whatever you choose, pick a config and commit it to the repo. The whole point is that the decision gets made once. Bikeshedding over brace placement in code review is a waste of everyone's afternoon.
A note on when not to format
Don't blanket-reformat a legacy file in the same commit as a logic change. The diff becomes noise and reviewers can't see what actually changed. If you need to introduce formatting to an old codebase, do it in a dedicated commit — ideally one that touches nothing else — so history stays readable. Some teams add that commit to a git blame ignore list so it doesn't muddy the record of who wrote what.
Formatting isn't glamorous, but consistent code is faster to read, faster to review, and less prone to the silly mistakes that hide in messy indentation. Set it up once, lean on a beautifier for the ad-hoc stuff, and spend your energy on the problems that actually need thinking.
Photo by Florian Olivo on Unsplash