cheat sheet

Prettier

Opinionated code formatter for JS/TS/CSS/HTML/JSON/Markdown. Covers config options, .prettierignore, VS Code integration, ESLint compatibility, lint-staged, and CI usage.

Prettier

What it is

Prettier is an opinionated code formatter for JavaScript, TypeScript, CSS, HTML, JSON, Markdown, and more. Instead of enforcing style through linting rules, it reprints your code from scratch according to its own rules. The key benefit: zero bikeshedding — everyone on the team gets identical formatting automatically.

Install

bash
npm install -D prettier

# Verify
npx prettier --version

Output:

text
3.3.3

Format files

bash
# Format all files in src/ (overwrites in place)
npx prettier --write src/

# Format the whole project
npx prettier --write .

# Check formatting without modifying files (exits 1 if any file differs)
npx prettier --check .

# Check a specific file
npx prettier --check src/index.ts

# Print the formatted output to stdout (no write)
npx prettier src/index.ts

Output (--check with issues):

text
Checking formatting...
[warn] src/app.ts
[warn] src/utils/helpers.ts
[warn] Code style issues found in 2 files. Run Prettier with --write to fix.

Output (--check clean):

text
Checking formatting...
All matched files use Prettier code style!

.prettierrc configuration

Prettier supports .prettierrc (JSON/YAML), .prettierrc.json, .prettierrc.yaml, .prettierrc.js, or a prettier key in package.json.

json
// .prettierrc
{
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": false,
  "quoteProps": "as-needed",
  "jsxSingleQuote": false,
  "trailingComma": "all",
  "bracketSpacing": true,
  "bracketSameLine": false,
  "arrowParens": "always",
  "endOfLine": "lf",
  "embeddedLanguageFormatting": "auto"
}

Key options explained

OptionDefaultDescription
printWidth80Target line length (a suggestion, not a hard cap)
tabWidth2Number of spaces per indent level
useTabsfalseIndent with tabs instead of spaces
semitrueAdd semicolons at statement ends
singleQuotefalseUse single quotes instead of double
trailingComma"all""all" adds trailing commas everywhere valid in ES5+
bracketSpacingtrueSpaces inside object braces: { foo: bar }
arrowParens"always"Always include parens in arrow functions: (x) => x
endOfLine"lf""lf", "crlf", "cr", or "auto"

.prettierignore

Uses the same syntax as .gitignore.

text
# .prettierignore
node_modules/
dist/
build/
.next/
coverage/
*.min.js
*.min.css
CHANGELOG.md
public/

Per-file-type overrides

json
// .prettierrc
{
  "semi": true,
  "singleQuote": false,
  "overrides": [
    {
      "files": "*.md",
      "options": {
        "proseWrap": "always",
        "printWidth": 80
      }
    },
    {
      "files": ["*.json", "*.yaml", "*.yml"],
      "options": {
        "tabWidth": 2
      }
    },
    {
      "files": "*.css",
      "options": {
        "singleQuote": true
      }
    }
  ]
}

Editor integration — VS Code

Install the Prettier - Code formatter extension (esbenp.prettier-vscode), then configure .vscode/settings.json:

json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[markdown]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

Prettier + ESLint

ESLint has style rules that conflict with Prettier's output. Use eslint-config-prettier to disable all ESLint formatting rules and let Prettier own code style.

bash
npm install -D eslint-config-prettier

Output: (none — exits 0 on success)

javascript
// eslint.config.js (flat config, v9)
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import prettierConfig from "eslint-config-prettier";

export default tseslint.config(
  js.configs.recommended,
  ...tseslint.configs.recommended,
  prettierConfig, // must be last — disables style rules that conflict with Prettier
  {
    rules: {
      // Your non-style rules here
      "no-console": "warn",
      eqeqeq: "error",
    },
  }
);

eslint-config-prettier only disables conflicting rules; it does not run Prettier. Prettier is a separate formatter step, not an ESLint rule.

Pre-commit hook with lint-staged

bash
npm install -D husky lint-staged
npx husky init

Output: (none — exits 0 on success)

Add to package.json:

json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix --max-warnings 0",
      "prettier --write"
    ],
    "*.{css,scss,html,json,yaml,yml,md}": [
      "prettier --write"
    ]
  }
}

Add to .husky/pre-commit:

bash
npx lint-staged

Output: (none — exits 0 on success)

Now every git commit will format staged files before committing.

package.json scripts

json
{
  "scripts": {
    "format": "prettier --write .",
    "format:check": "prettier --check ."
  }
}

CI usage (GitHub Actions)

yaml
- name: Check formatting
  run: npx prettier --check .

This exits with code 1 if any file differs from Prettier's output, failing the CI pipeline.

Supported languages and notable plugins

Prettier supports these languages natively:

LanguageExtensions
JavaScript.js, .cjs, .mjs
TypeScript.ts, .tsx
JSX.jsx
JSON / JSONC.json, .jsonc
CSS / SCSS / Less.css, .scss, .less
HTML.html
Vue.vue
Markdown.md, .mdx
YAML.yaml, .yml
GraphQL.graphql

Notable plugins

bash
# Sort Tailwind CSS classes automatically
npm install -D prettier-plugin-tailwindcss

# Format SQL
npm install -D prettier-plugin-sql

# Format .astro files
npm install -D prettier-plugin-astro

# Sort package.json keys
npm install -D prettier-plugin-packagejson

Output: (none — exits 0 on success)

json
// .prettierrc with plugins
{
  "plugins": [
    "prettier-plugin-tailwindcss",
    "prettier-plugin-astro"
  ],
  "overrides": [
    {
      "files": "*.astro",
      "options": { "parser": "astro" }
    }
  ]
}

Ignoring specific code blocks

javascript
// prettier-ignore disables formatting for the next expression
// prettier-ignore
const matrix = [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1,
];
html
<!-- prettier-ignore -->
<div    class="messy"   >untouched</div>
css
/* prettier-ignore */
.selector { color:   red; }

How Prettier works — the AST round-trip

Prettier is fundamentally different from ESLint's stylistic rules. ESLint sees source as text and rewrites a span of characters. Prettier parses the file into an Abstract Syntax Tree (AST), throws away the original whitespace, then prints the AST back out using its own layout algorithm (an adaptation of Wadler's "A prettier printer"). This is why Prettier output is deterministic and why two semantically equivalent files always format identically — there is no original whitespace to preserve.

Consequence: Prettier cannot enforce rules that depend on the dropped formatting (no no-multiple-empty-lines, no indent rule). It also means a file with a parse error fails completely — there is no "fix this one line" mode.

bash
# See what Prettier would do — print to stdout
npx prettier src/messy.ts

Output:

text
export function add(a: number, b: number): number {
  return a + b;
}

--check vs --write vs --list-different

bash
# --check: dry-run for CI. Exit 0 if all formatted, 1 if any differ.
npx prettier --check .

# --write: rewrite files in place
npx prettier --write .

# --list-different: like --check but only prints filenames (no header)
npx prettier --list-different .

# Show only files Prettier WOULD change, then format them
npx prettier --list-different src/ | xargs npx prettier --write

Output (--list-different with mis-formatted files):

text
src/app.ts
src/utils/helpers.ts

Resolving the effective config for a file

bash
# Print which config options Prettier will use for a path
npx prettier --find-config-path src/app.ts

# Or dump the merged config
npx prettier --config-precedence prefer-file src/app.ts --debug-check

Output:

text
.prettierrc

Full configuration reference

The full Prettier option set (3.x):

OptionDefaultNotes
printWidth80Target line width. Soft cap — long strings/identifiers may exceed.
tabWidth2Spaces per indent level.
useTabsfalseIndent with tabs instead.
semitruePrint semicolons at statement ends.
singleQuotefalse'foo' instead of "foo" in JS.
quoteProps"as-needed""as-needed" | "consistent" | "preserve".
jsxSingleQuotefalseSingle quotes in JSX attributes.
trailingComma"all""all" | "es5" | "none".
bracketSpacingtrue{ foo: bar } vs {foo: bar}.
bracketSameLinefalsePut > of multi-line JSX at end of last line.
arrowParens"always"Always wrap single-arg arrow params in parens.
endOfLine"lf""lf" | "crlf" | "cr" | "auto".
embeddedLanguageFormatting"auto"Format embedded code (template literals tagged html/css/etc).
htmlWhitespaceSensitivity"css""css" | "strict" | "ignore".
vueIndentScriptAndStylefalseIndent <script> and <style> blocks in Vue SFC.
singleAttributePerLinefalseOne JSX/HTML attribute per line.
proseWrap"preserve""always" | "never" | "preserve" for Markdown.
objectWrap"preserve"(3.4+) Whether to collapse short objects.
experimentalTernariesfalse(3.1+) Curried-style ternary formatting.
experimentalOperatorPosition"end"(3.4+) Line-leading operator style.
plugins[]Array of plugin package names.

Object form in package.json

json
{
  "prettier": {
    "printWidth": 100,
    "singleQuote": true,
    "trailingComma": "all"
  }
}

JavaScript config (lets you require plugins programmatically)

javascript
// prettier.config.js
export default {
  printWidth: 100,
  semi: true,
  singleQuote: true,
  trailingComma: "all",
  plugins: ["prettier-plugin-tailwindcss"],
};

Prettier vs ESLint — the canonical split

The right mental model: Prettier handles formatting, ESLint handles code quality. A formatting rule cares about whitespace, line wrapping, and quote style. A code-quality rule cares about correctness — unused variables, missing return values, dangerous comparisons, async pitfalls.

ConcernOwner
Indentation, line width, quote style, trailing commaPrettier
Semicolon enforcement (style)Prettier
no-unused-vars, no-undef, prefer-constESLint
react-hooks/exhaustive-depsESLint
Import orderingESLint (eslint-plugin-import) or Prettier plugin (@trivago/prettier-plugin-sort-imports)
Sorting package.json keysPrettier plugin (prettier-plugin-packagejson)

eslint-config-prettier

Disables every ESLint rule that conflicts with Prettier. Spread it last in your flat config so it can override any earlier rule arrays.

javascript
// eslint.config.js
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import prettierConfig from "eslint-config-prettier/flat";

export default tseslint.config(
  js.configs.recommended,
  ...tseslint.configs.recommended,
  prettierConfig,            // MUST be last
  {
    rules: {
      "no-console": "warn",
      eqeqeq: "error",
    },
  },
);

Check what eslint-config-prettier is turning off:

bash
npx eslint-config-prettier src/app.ts

Output (no conflicts):

text
No rules that are unnecessary or conflict with Prettier were found.

Output (with conflicts):

text
The following rules are unnecessary or might conflict with Prettier:

- max-len
- semi

The following rules are enabled but cannot be automatically checked. See:
https://github.com/prettier/eslint-config-prettier#special-rules

- arrow-body-style
- prefer-arrow-callback

eslint-plugin-prettier — and why to avoid it

eslint-plugin-prettier runs Prettier inside ESLint and reports format mismatches as ESLint errors. It "just works" but has real costs:

  • Slow: every ESLint run also does a full Prettier pass per file.
  • Noisy errors: a single block reformat creates dozens of ESLint errors.
  • Confusing autofix: eslint --fix and prettier --write both modify the file with subtle ordering issues.

The canonical setup (per Prettier docs) is to run Prettier and ESLint as separate tools — Prettier in its own script, ESLint with eslint-config-prettier to step out of the way.

Plugins ecosystem

Plugins extend Prettier with formatters for new languages or pre-/post-processing for existing ones. Plugins are loaded via the plugins array in your config; order matters when multiple plugins want to touch the same file.

prettier-plugin-tailwindcss — sort Tailwind utility classes

The official Tailwind plugin reorders class="..." attributes into Tailwind's recommended order (layout → spacing → typography → colour → state). Eliminates "is the duplicate mt-4 last or first?" bikeshedding.

bash
npm install -D prettier-plugin-tailwindcss

Output: (none — exits 0 on success)

json
// .prettierrc
{
  "plugins": ["prettier-plugin-tailwindcss"],
  "tailwindFunctions": ["clsx", "cn", "twMerge"]
}

Before:

jsx
<div className="text-white p-4 bg-blue-500 hover:bg-blue-700 flex items-center">

After:

jsx
<div className="flex items-center bg-blue-500 p-4 text-white hover:bg-blue-700">

prettier-plugin-packagejson — sort package.json keys

Reorders package.json fields into the npm-canonical order (name, version, description, ...). Pairs well with the files array sort.

bash
npm install -D prettier-plugin-packagejson

Output: (none — exits 0 on success)

@trivago/prettier-plugin-sort-imports and @ianvs/prettier-plugin-sort-imports

Sorts ES module imports by glob group. The @ianvs fork supports more options (side-effects last, blank lines between groups, regex-based grouping).

json
{
  "plugins": ["@ianvs/prettier-plugin-sort-imports"],
  "importOrder": [
    "^(react|react-dom)$",
    "<THIRD_PARTY_MODULES>",
    "^@/(.*)$",
    "^[./]"
  ],
  "importOrderSeparation": true,
  "importOrderSortSpecifiers": true
}

prettier-plugin-astro, prettier-plugin-svelte, @prettier/plugin-php, prettier-plugin-sh

One plugin per language that Prettier core doesn't ship. Each registers an additional parser; use overrides to map file extensions to it.

json
{
  "plugins": ["prettier-plugin-astro", "prettier-plugin-sh"],
  "overrides": [
    { "files": "*.astro", "options": { "parser": "astro" } },
    { "files": ["*.sh", "*.bash"], "options": { "parser": "sh" } }
  ]
}

Prettier vs Biome — formatting only

Biome ships a Rust formatter that is intentionally close to Prettier output, but not identical. Differences worth knowing:

ConcernPrettierBiome
SpeedSingle-threaded Node; 1–5 s for 1k filesMulti-threaded Rust; sub-second for 1k files
Markdown / YAML / HTMLYesNo (JS/TS/JSON/CSS/GraphQL only)
Plugin ecosystemMature (Tailwind, sort-imports, packagejson, …)Limited (no official Tailwind sort yet — community plugins via WASM)
arrowParens"always" default"always" default
Trailing comma"all" default"all" default (matched recently)
Quote styleDouble defaultDouble default
Config file.prettierrcbiome.json
Editor supportUniversalVS Code, Zed, JetBrains

Most teams that switch to Biome do it for speed + single-config simplicity, then add a Prettier-only step for the file types Biome doesn't support yet (Markdown, YAML in many setups). See the biome page for the full migration recipe.

Common pitfalls

  • eslint --fix and prettier --write fight each other — install eslint-config-prettier so ESLint's style rules step out of the way.
  • Prettier reformats Markdown tables ugly — set proseWrap: "preserve" (default) and remember Prettier does NOT align cells in tables; it normalises pipes only.
  • .prettierignore does not inherit from .gitignore — Prettier 3+ added --ignore-path .gitignore if you want unified ignores; otherwise list paths explicitly.
  • Plugin order changes outputprettier-plugin-tailwindcss must come last when used with @ianvs/prettier-plugin-sort-imports, otherwise Tailwind sort runs before import sort and gets clobbered.
  • CRLF line endings on Windows — pair "endOfLine": "lf" with * text=auto eol=lf in .gitattributes to avoid format diffs caused by git checkout.
  • prettier-ignore not respected in HTML — must be the comment immediately before the element, with no intervening blank lines.
  • Prettier 3 changed trailingComma default to "all" — bumping from v2 reformats every callsite. Either accept the diff in one commit or pin trailingComma: "es5" temporarily.
  • Cache file checked in — Prettier 3 added --cache; the cache lives at node_modules/.cache/prettier/.prettier-cache. Don't commit it.

Real-world recipes

Format only staged files

bash
# git-staged paths (works in any pre-commit context)
git diff --name-only --cached --diff-filter=ACMR \
  | grep -E '\.(js|jsx|ts|tsx|json|md|css)$' \
  | xargs --no-run-if-empty npx prettier --write

# re-stage after writing
git add -u

Output:

text
src/app.ts 24ms
src/utils.ts 11ms (unchanged)

Fast incremental formatting with --cache

bash
npx prettier --check --cache --cache-strategy metadata .

Output (warm cache):

text
Checking formatting...
All matched files use Prettier code style!

--cache-strategy metadata (default) uses file mtime/size for invalidation — very fast. --cache-strategy content hashes file contents — slower but survives touch.

Ignore a generated file pattern, but include a specific file

.prettierignore:

text
dist/**
!dist/important-file.js

Output: (none — exits 0 on success)

Format a single file from stdin (useful for editor integrations)

bash
cat src/app.ts | npx prettier --stdin-filepath src/app.ts

Output:

text
export const greeting = "hello";

--stdin-filepath is what tells Prettier which parser to choose; without it, you'd have to pass --parser typescript manually.

Lock the Prettier version across the team

json
// package.json
{
  "devDependencies": { "prettier": "3.3.3" },
  "packageManager": "pnpm@9.12.1"
}

Pin Prettier with no ^ so a npm install on a colleague's machine never silently upgrades and reformats your entire repo.

CI: fail the build on format drift

yaml
# .github/workflows/ci.yml
- name: Prettier check
  run: npx prettier --check --cache .

For a friendlier failure message:

yaml
- name: Prettier check
  run: |
    if ! npx prettier --check --cache .; then
      echo "::error::Run 'npx prettier --write .' locally and commit."
      exit 1
    fi

See also

  • ESLint — pair via eslint-config-prettier
  • Biome — Rust-based linter+formatter alternative
  • Vite, Vitest — typical companions in a modern setup