BytePane

Code Beautifier: Format & Prettify Code in Any Language

Developer Tools17 min read

The PR That Cost Three Hours to Review

A senior engineer on a mid-size team pushed a refactor that touched 47 files. The PR diff showed 3,200 changed lines. The actual logic changes? 80 lines. The other 3,120 were indentation inconsistencies, mixed quote styles, and trailing whitespace — formatting noise accumulated across a codebase that had never enforced a style standard. Reviewers spent three hours parsing signal from noise, and two bugs slipped through.

That scenario drives home why code beautifiers exist. A code beautifier — also called a code formatter or prettifier — eliminates formatting as a variable. It parses source code into an Abstract Syntax Tree, discards all original whitespace decisions, and reprints the code with a single, enforced style. After adoption, every diff shows only semantic changes. Review time collapses.

According to npm registry data, Prettier downloads 80–81 million packages per week as of 2026 — making it one of the most downloaded developer tools in the entire npm ecosystem. The number reflects how widespread automated formatting has become. This article covers how these tools work under the hood, how they compare, and how to integrate them into your workflow.

Key Takeaways

  • AST-based formatters parse code into a syntax tree and reprint from scratch — output is deterministic regardless of input style.
  • Biome is ~40x faster than Prettier on large codebases (Rust vs JavaScript runtime). Prettier still dominates at 81M weekly downloads vs Biome's 2–7.5M.
  • Every major language has an idiomatic formatter: Black (Python), gofmt (Go), rustfmt (Rust), clang-format (C/C++).
  • Formatter ≠ Linter ≠ Minifier. Run all three in sequence: format for style, lint for correctness, minify for production output.
  • Formatting has zero impact on runtime performance — compilers operate on the AST, not whitespace.

How a Code Beautifier Actually Works: The AST Pipeline

The naive implementation of a code formatter is regex substitution: find tabs, replace with spaces; find double quotes, replace with single. This approach fails immediately on anything more complex than a single line. Nested structures, multiline strings, template literals, and operator precedence all break regex-based formatting in ways that are hard to detect and catastrophic when they occur.

Production code beautifiers use a three-stage pipeline:

  1. Parse: The source code is lexed into tokens (keywords, operators, identifiers, literals) then parsed into an Abstract Syntax Tree — a tree structure representing the semantic meaning of the code independent of whitespace.
  2. Transform (optional): Some formatters normalize certain constructs at this stage — converting arrow functions to specific forms, reordering imports, or sorting object keys.
  3. Print: The AST is walked and reprinted as text, with the formatter controlling every whitespace decision: indentation depth, line breaks, spacing around operators, trailing commas, bracket placement.

Because the AST encodes what code means rather than what it looks like, the formatter can make decisions that would be impossible with regex. It knows that a function call's arguments should break across lines if they exceed the print width, and that an arrow function body is a single expression vs. a block. Biome's formatter, for example, uses shared ASTs between its linter and formatter — the same parse result feeds both passes, which is part of why it avoids redundant work and runs significantly faster.

// Input: inconsistently formatted JavaScript
const   obj={name:'Alice',  age:30,roles:  ['admin','editor']}

// After Prettier (print width 80, trailing commas, double quotes):
const obj = {
  name: "Alice",
  age: 30,
  roles: ["admin", "editor"],
};

// The formatter did not change any semantics.
// It only changed whitespace — deterministically, always the same output.

Formatter vs Linter vs Minifier: Three Different Tools

These three tool categories are frequently confused and sometimes conflated. They solve different problems and belong at different points in a development pipeline.

Tool TypeWhat It ControlsChanges Semantics?Run WhenExample Tools
FormatterIndentation, spacing, line breaks, quote styleNoOn save, pre-commit, CI checkPrettier, Biome, Black, gofmt
LinterUnused vars, type errors, best practices, securitySometimes (--fix)On save, CI gateESLint, Pylint, Clippy, go vet
MinifierFile size, dead code elimination, variable renamingNo (semantics preserved)Build time onlyTerser, esbuild, LightningCSS

The recommended pipeline: format first, lint second, minify at build. Running the linter before the formatter means you might fix a linting issue only to have the formatter move it to a different line, requiring another lint pass. Format first keeps the order clean.

Note that some tools are expanding their scope. Biome combines formatting and linting in a single binary, which eliminates the format-then-lint pipeline overhead. ESLint's formatting rules were historically used as a formatter substitute, but the ESLint team has deprecated those rules in favor of dedicated formatters — a separation of concerns the community largely endorses.

Language-Specific Formatters: The Idiomatic Choice Per Ecosystem

Each language community has converged on one or two standard formatters. Deviating from the community default creates friction — your CI configuration, editor integrations, and onboarding docs all reference the standard tool. Here is the canonical reference:

JavaScript & TypeScript — Prettier / Biome

Prettier (51.8k GitHub stars, 81M weekly npm downloads) is the incumbent. It handles JS, TS, JSX, TSX, JSON, GraphQL, YAML, Markdown, and CSS in one tool. Its configuration surface is intentionally tiny — that's the point. Biome (24k+ stars, up to 7.5M weekly downloads) is the challenger: written in Rust, 40× faster on large codebases, and supports linting + formatting in a single binary. Biome is now used by AWS, Google, Microsoft, Cloudflare, and Vercel for their internal toolchains.

# Prettier
npx prettier --write "src/**/*.{ts,tsx,json}"

# Check mode (CI) — exits non-zero if formatting differs
npx prettier --check "src/**/*.{ts,tsx}"

# Biome (format + lint in one pass)
npx @biomejs/biome check --apply ./src

# Biome format only
npx @biomejs/biome format --write ./src

Python — Black / Ruff

Black is "the uncompromising Python code formatter." Its design philosophy: near-zero configuration means near-zero style debates. Black enforces 88-character line length (not 79 — a deliberate deviation from PEP 8 to avoid unnecessary wrapping), double quotes, and trailing commas in multi-line collections. Ruff (written in Rust) is orders of magnitude faster and includes a formatter compatible with Black's output, plus a full linter.

# Black
pip install black
black src/                    # format in-place
black --check src/            # CI check mode
black --diff src/             # show diff without writing

# Ruff formatter (Black-compatible output)
pip install ruff
ruff format src/              # format
ruff format --check src/      # CI check
ruff check src/               # lint (separate pass)

Go — gofmt / goimports

gofmt ships with the Go toolchain. It is not optional in any practical sense — the Go community considers unformatted code invalid. Unlike Black or Prettier, gofmt is conservative: it normalizes indentation and spacing but does not reformat line lengths. goimports extends gofmt to also add/remove import statements automatically.

# gofmt
gofmt -w ./...               # format in-place, all packages
gofmt -l ./...               # list files that differ
gofmt -d main.go             # show diff

# goimports (superset of gofmt)
go install golang.org/x/tools/cmd/goimports@latest
goimports -w ./...

# CI check (exit 1 if any files are unformatted)
test -z "$(gofmt -l ./...)"  || (gofmt -d ./... && exit 1)

Rust — rustfmt

rustfmt ships with the Rust toolchain via rustup and is invoked via cargo fmt. Unlike gofmt, rustfmt is a full pretty-printer — it will aggressively reformat line lengths and break long expressions across multiple lines. Configuration lives in rustfmt.toml.

cargo fmt                    # format all crates
cargo fmt --check            # CI: exit 1 if any changes would be made
cargo fmt -- --emit files    # write formatted files without changing originals

C / C++ — clang-format

clang-format is part of the LLVM project. It supports multiple preset styles (LLVM, Google, Chromium, Mozilla, WebKit) and extensive per-rule configuration via .clang-format. Major open source projects including Chrome, LLVM, and the Linux kernel use clang-format for style enforcement.

# Format in-place
clang-format -i src/*.cpp src/*.h

# Use Google style
clang-format -style=Google -i src/*.cpp

# CI check
clang-format --dry-run --Werror src/*.cpp

Prettier vs Biome vs dprint: Performance Benchmarks

The performance gap between JavaScript-based and Rust-based formatters is not marginal. On a codebase of 10,000 files, the difference is measured in seconds vs. fractions of a second. Per Biome's published benchmarks (tested on an Intel i7 1270P with 171,127 lines across 2,104 files):

ToolRuntime10,000 filesSpeedup vs PrettierWeekly Downloads
PrettierNode.js (V8)~12.1s1× (baseline)81M
BiomeRust (native)~0.3s~40×2–7.5M
dprintRust + WASM plugins~1–2s~6–12×142–151k
Black (Python)CPythonN/A (Python only)N/A~30M+

On Apple Silicon (M1 Max, 10 cores), Biome's multi-threaded architecture pushes the gap to approximately 100× faster than Prettier on the same file set. For a team running Prettier on every commit in CI, switching to Biome can cut format-check time from 15–30 seconds to under a second — a meaningful improvement on tight CI cost budgets.

That said, Prettier's 81M vs Biome's 7.5M weekly download gap reflects real inertia: Prettier's ecosystem of editor integrations, shared configs, and plugin support is mature. Biome 2.x added 423+ lint rules with type-aware linting (January 2026), closing the feature gap. Migration is practical for greenfield projects or teams willing to audit their Prettier plugin dependencies.

dprint occupies a different niche: pluggable architecture with language-specific WASM plugins and incremental formatting (only changed files). For polyglot monorepos with significant non-JS content, dprint's incremental mode can outperform both in real-world CI usage despite the lower raw throughput.

Opinionated vs Configurable: Which Philosophy Wins?

The "opinionated formatter" design deliberately removes configuration options. Prettier has roughly a dozen settings; Black has fewer. The argument: if every team could configure everything, style discussions would continue indefinitely, just inside a config file instead of a code review thread.

Thoughtworks Technology Radar lists "opinionated and automated code formatting" as an established technique — meaning the industry has broadly validated the approach. The practical evidence: teams that adopt opinionated formatters report that style debates simply disappear from code review. The tool becomes the arbiter, and engineers stop arguing about it because there is nothing to argue about.

Configurable formatters like clang-format and dprint serve different use cases: systems codebases with established style guides that predate formatters, or teams integrating with external style requirements (e.g., contributing to Linux kernel code that must follow kernel style). For new projects, start with the opinionated tool and override only what is technically required.

Editor & CI/CD Integration

VS Code: Format on Save

// .vscode/settings.json (project-level — commit this)
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[python]": {
    "editor.defaultFormatter": "ms-python.black-formatter"
  },
  "[go]": {
    "editor.defaultFormatter": "golang.go"
  },
  "prettier.requireConfig": true  // only format if .prettierrc exists
}

Neovim: conform.nvim

conform.nvim is the standard Neovim formatter plugin, supporting Prettier, Black, gofmt, rustfmt, and 50+ others with language-specific routing.

-- lua/plugins/conform.lua
require("conform").setup({
  formatters_by_ft = {
    javascript = { "prettier" },
    typescript = { "prettier" },
    python     = { "black" },
    go         = { "goimports", "gofmt" },
    rust       = { "rustfmt" },
    lua        = { "stylua" },
  },
  format_on_save = {
    timeout_ms = 2000,
    lsp_fallback = true,
  },
})

Pre-commit Hook: lint-staged

// package.json
{
  "lint-staged": {
    "*.{js,ts,tsx,json,md}": "prettier --write",
    "*.py": "black",
    "*.go": "gofmt -w"
  }
}

// .husky/pre-commit
#!/bin/sh
npx lint-staged

GitHub Actions: CI Formatting Gate

# .github/workflows/format-check.yml
name: Format Check
on: [pull_request]

jobs:
  format:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22' }
      - run: npm ci
      - run: npx prettier --check "src/**/*.{ts,tsx,json}"
      # For Python:
      - uses: actions/setup-python@v5
        with: { python-version: '3.12' }
      - run: pip install black && black --check src/

The CI gate runs in check mode — it exits non-zero if any file differs from formatted output. It does not modify files. The developer is responsible for running the formatter locally (via editor integration or pre-commit hook) before pushing. This creates the right incentive: formatting errors are caught before the PR is opened, not after.

SQL Formatting: The Underserved Case

SQL formatters are less standardized than their application-language counterparts. There is no community-mandated equivalent of gofmt for SQL. Common options are sqlfluff (Python, highly configurable, supports 20+ SQL dialects including BigQuery, Snowflake, Spark) and sql-formatter (npm, 1.7M weekly downloads).

BytePane's SQL Formatter handles browser-side formatting for quick query review. For CI enforcement in a dbt or raw-SQL codebase, sqlfluff integrates directly with pre-commit and GitHub Actions.

# sqlfluff — format a directory of SQL files
pip install sqlfluff
sqlfluff fix --dialect postgres models/

# sql-formatter (Node.js)
npx sql-formatter --language postgresql query.sql

# The canonical example: before and after
-- Before:
SELECT u.id,u.name,o.total FROM users u JOIN orders o ON u.id=o.user_id WHERE o.total>100

-- After sqlfluff:
SELECT
    u.id,
    u.name,
    o.total
FROM users AS u
INNER JOIN orders AS o ON u.id = o.user_id
WHERE o.total > 100

CSS Formatting: Prettier Handles It, Stylelint Checks It

Prettier formats CSS, SCSS, Less, and PostCSS out of the box. Stylelint (8.2M weekly downloads) is the standard linter for CSS — it catches things Prettier cannot, like invalid property values, non-existent selectors, and accessibility violations in CSS. For property ordering specifically (a controversial style choice), stylelint-order enforces a consistent sequence.

For a deeper dive into CSS-specific formatting tools and CSS minification pipelines, read our guide on CSS Formatters.

Frequently Asked Questions

What is a code beautifier?

A code beautifier (also called a formatter or prettifier) parses source code into an Abstract Syntax Tree, discards all original whitespace, and reprints the code with consistent indentation, line breaks, and spacing. Output is semantically identical but uniformly styled regardless of input formatting.

What is the difference between a code formatter and a linter?

A formatter controls style: indentation, line length, quote style, trailing commas. A linter enforces correctness and best practices: unused variables, unreachable code, type errors. Tools like ESLint do both; Prettier does formatting only. Most teams run both in their CI pipeline — format first, then lint.

Is Biome replacing Prettier?

Biome is gaining significant traction — 24k+ GitHub stars and up to 7.5M weekly downloads as of 2026 — but has not replaced Prettier (81M weekly downloads). Biome is ~40× faster and combines linting + formatting. Teams migrating typically do so for CI speed gains on large monorepos. Prettier's plugin ecosystem remains broader.

Which formatter should I use for Python?

Black is the de facto standard: minimal configuration, double quotes, 88-char line length. Ruff (Rust-based) is a faster alternative that also includes a linter and produces Black-compatible output. Use Black for simplicity; use Ruff if formatting speed in CI is a constraint.

How do I run a code formatter automatically on save?

In VS Code: set "editor.formatOnSave": true and "editor.defaultFormatter": "esbenp.prettier-vscode" in settings.json. In Neovim: use conform.nvim with format_on_save. For CI enforcement, add a prettier --check step that fails if formatting differs.

Does code formatting affect runtime performance?

Zero impact. Compilers and interpreters operate on the AST or bytecode, not source whitespace. The formatted and unformatted versions of identical code produce the same machine instructions. Format freely — it is a developer experience improvement only.

What is an opinionated code formatter?

An opinionated formatter like Prettier or Black offers very few configuration options by design. The goal: eliminate style debates entirely. Thoughtworks Technology Radar lists opinionated and automated code formatting as an established practice. Teams that adopt opinionated formatters report that style reviews disappear from code review permanently.

Format Code Instantly in Your Browser

BytePane's code formatter handles JavaScript, TypeScript, JSON, CSS, SQL, HTML, and more — paste your code and get clean, consistently formatted output in under a second. No installation, no sign-up.

Open Code Beautifier

Related Articles