iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Format on Save

Auto-formatting on save eliminates an entire class of bikeshed code review. Wire Prettier (or the language’s native formatter) into VS Code so every save produces the same output across the team — no more “please fix the indentation” comments.

Prettier, ESLint, format-on-save

EXAMPLE
// 1) settings.json — workspace defaults (commit this with the repo)
// .vscode/settings.json
{
    "editor.formatOnSave": true,
    "editor.formatOnPaste": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint":         "explicit",
        "source.organizeImports":        "explicit",
        "source.addMissingImports":     "explicit"
    },
    "editor.tabSize": 2,
    "editor.insertSpaces": true,
    "files.trimTrailingWhitespace": true,
    "files.insertFinalNewline":    true,
    "files.eol": "\n",
    "prettier.requireConfig": true
}

// 'requireConfig: true' means Prettier only runs if a config file exists in the repo —
// stops random formatting in repos that haven't opted in.

// 2) .prettierrc.json — committed alongside the project
{
    "semi":             true,
    "singleQuote":      true,
    "trailingComma":    "all",
    "printWidth":       100,
    "tabWidth":         2,
    "useTabs":          false,
    "bracketSpacing":   true,
    "arrowParens":      "always",
    "plugins":          ["prettier-plugin-tailwindcss"]
}

// 3) Per-language overrides
// .vscode/settings.json
{
    "[python]": {
        "editor.defaultFormatter": "ms-python.black-formatter",
        "editor.tabSize": 4
    },
    "[go]": {
        "editor.defaultFormatter": "golang.go",
        "editor.tabSize": 4,
        "editor.insertSpaces": false
    },
    "[rust]": {
        "editor.defaultFormatter": "rust-lang.rust-analyzer"
    },
    "[markdown]": {
        "editor.formatOnSave": false,            // markdown formatters often reflow prose; opt in per-file
        "editor.wordWrap": "on"
    }
}

// 4) Recommended extensions list (gets a tooltip prompt on first open)
// .vscode/extensions.json
{
    "recommendations": [
        "esbenp.prettier-vscode",
        "dbaeumer.vscode-eslint",
        "ms-python.python",
        "ms-python.black-formatter",
        "golang.go",
        "rust-lang.rust-analyzer"
    ]
}

// 5) Prettier vs ESLint — pick one for each concern
// • Prettier:  formatting (whitespace, semicolons, line length, quote style)
// • ESLint:    code quality (unused vars, hooks rules, anti-patterns, security)
// They should NOT fight. Use eslint-config-prettier to disable ESLint's stylistic rules.

// eslint.config.js (flat config, ESLint 9+)
import prettier from 'eslint-config-prettier';
export default [
    js.configs.recommended,
    tseslint.configs.recommended,
    react.configs.recommended,
    prettier,                              // MUST be last; disables stylistic ESLint rules that Prettier owns
];

// 6) Native formatters — language-aware, fast
//   gofmt / goimports          — Go
//   rustfmt                     — Rust
//   black + isort + ruff       — Python
//   dart format                 — Dart / Flutter
//   ktlint / ktfmt              — Kotlin
//   swift-format                — Swift
//   stylua                      — Lua
//   shfmt                        — shell
//   sql-formatter / pg_format   — SQL
//
// Most have a VS Code extension that wires them as the default formatter.

// 7) Save without formatting — useful escape hatch
//   Ctrl+K S          (Save Without Formatting)
// Use sparingly; if you find yourself doing it often, the formatter config is wrong.

// 8) Format only the selection
//   Ctrl+K Ctrl+F     (Format Selection)

// 9) Format on type
//   editor.formatOnType: true → reformats as you type a delimiter (closing brace, semicolon)
// Powerful but can interrupt; default to off and turn on per-language if you like it.

// 10) EditorConfig — fallback when a teammate isn't on VS Code
// .editorconfig (committed)
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.{go,gofmt}]
indent_style = tab

[*.{py}]
indent_size = 4

// VS Code, IntelliJ, Vim, Emacs — all read .editorconfig. Set the baseline once for the whole team.

// 11) Format on save vs on commit
// • Format on save → instant feedback; might churn unrelated lines if a teammate didn't format
// • Pre-commit hook (lefthook, Husky + lint-staged) → safety net for non-VS Code users

// .lintstagedrc.json
{
    "*.{ts,tsx,js,jsx,json,css,md,yaml,yml}": ["prettier --write"],
    "*.{ts,tsx,js,jsx}":                       ["eslint --fix"],
    "*.{py}":                                   ["black", "ruff --fix"],
    "*.{go}":                                   ["gofmt -w", "goimports -w"]
}

// 12) Project-wide formatting before adopting it on a big repo
npx prettier --write "**/*.{ts,tsx,js,jsx,json,md,css}"
npx eslint . --fix

// Then commit AS ONE commit ('chore: format codebase') so git blame is cleaner.
// Add the commit hash to .git-blame-ignore-revs so git blame skips it on hosted UIs.

// 13) Disable formatter for a region
// JS/TS:
// prettier-ignore — pragma comment
// before a line / block
// Useful for ASCII art tables, manual alignment in lookup tables.
// prettier-ignore
const matrix = [
    [ 1, 0, 0 ],
    [ 0, 1, 0 ],
    [ 0, 0, 1 ],
];

// 14) Multi-formatter conflicts
// Symptom: file reformats on every save (back and forth between two formatters).
// Causes:
//   • Two extensions both claiming defaultFormatter for the same file type
//   • ESLint stylistic rule disagrees with Prettier — install eslint-config-prettier
//   • Different .editorconfig vs .prettierrc settings — make them consistent
// Debug:
//   Command Palette → 'Format Document With…' → pick once; VS Code remembers

// 15) Format on save in monorepos
//   • Use per-package .prettierrc when settings genuinely differ
//   • Otherwise put one config at the repo root and pin tools as workspace devDependencies
//   • Tool versions matter — Prettier 2.x and 3.x format differently; commit the lockfile

// 16) Common bugs
//   • formatOnSave is on but nothing happens → defaultFormatter is missing or wrong; check Output panel
//   • Format strips a line that you wanted — escape with // prettier-ignore
//   • Settings.json has a typo in defaultFormatter — VS Code silently falls back to none
//   • Multiple formatters for the same file → file flickers; pick one
//   • Prettier disagrees with team style — change the config once and push, don't argue per file
//   • CR / LF on Windows clashes with Unix endings — set files.eol and .editorconfig
//   • organizeImports removes something used implicitly (CSS side-effects) — annotate with @ts-ignore or import side-effects explicitly

Why it matters

Wire Prettier (or a language-native formatter) into editor.formatOnSave with a committed config so every teammate produces the same output. Combine with ESLint for code-quality concerns, eslint-config-prettier to keep them from fighting, and a .git-blame-ignore-revs entry for the one-time “format the whole repo” commit.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode"
}
Try it Yourself »

Exercise

Enable auto-format on save.

"editor. ": true

Discussion

Loading…