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
npm install -D prettier
# Verify
npx prettier --version
Output:
3.3.3
Format files
# 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):
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):
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.
// .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
| Option | Default | Description |
|---|---|---|
printWidth | 80 | Target line length (a suggestion, not a hard cap) |
tabWidth | 2 | Number of spaces per indent level |
useTabs | false | Indent with tabs instead of spaces |
semi | true | Add semicolons at statement ends |
singleQuote | false | Use single quotes instead of double |
trailingComma | "all" | "all" adds trailing commas everywhere valid in ES5+ |
bracketSpacing | true | Spaces 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.
# .prettierignore
node_modules/
dist/
build/
.next/
coverage/
*.min.js
*.min.css
CHANGELOG.md
public/
Per-file-type overrides
// .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:
{
"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.
npm install -D eslint-config-prettier
Output: (none — exits 0 on success)
// 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-prettieronly disables conflicting rules; it does not run Prettier. Prettier is a separate formatter step, not an ESLint rule.
Pre-commit hook with lint-staged
npm install -D husky lint-staged
npx husky init
Output: (none — exits 0 on success)
Add to package.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:
npx lint-staged
Output: (none — exits 0 on success)
Now every git commit will format staged files before committing.
package.json scripts
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}
CI usage (GitHub Actions)
- 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:
| Language | Extensions |
|---|---|
| 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
# 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)
// .prettierrc with plugins
{
"plugins": [
"prettier-plugin-tailwindcss",
"prettier-plugin-astro"
],
"overrides": [
{
"files": "*.astro",
"options": { "parser": "astro" }
}
]
}
Ignoring specific code blocks
// prettier-ignore disables formatting for the next expression
// prettier-ignore
const matrix = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
];
<!-- prettier-ignore -->
<div class="messy" >untouched</div>
/* 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.
# See what Prettier would do — print to stdout
npx prettier src/messy.ts
Output:
export function add(a: number, b: number): number {
return a + b;
}
--check vs --write vs --list-different
# --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):
src/app.ts
src/utils/helpers.ts
Resolving the effective config for a file
# 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:
.prettierrc
Full configuration reference
The full Prettier option set (3.x):
| Option | Default | Notes |
|---|---|---|
printWidth | 80 | Target line width. Soft cap — long strings/identifiers may exceed. |
tabWidth | 2 | Spaces per indent level. |
useTabs | false | Indent with tabs instead. |
semi | true | Print semicolons at statement ends. |
singleQuote | false | 'foo' instead of "foo" in JS. |
quoteProps | "as-needed" | "as-needed" | "consistent" | "preserve". |
jsxSingleQuote | false | Single quotes in JSX attributes. |
trailingComma | "all" | "all" | "es5" | "none". |
bracketSpacing | true | { foo: bar } vs {foo: bar}. |
bracketSameLine | false | Put > 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". |
vueIndentScriptAndStyle | false | Indent <script> and <style> blocks in Vue SFC. |
singleAttributePerLine | false | One JSX/HTML attribute per line. |
proseWrap | "preserve" | "always" | "never" | "preserve" for Markdown. |
objectWrap | "preserve" | (3.4+) Whether to collapse short objects. |
experimentalTernaries | false | (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
{
"prettier": {
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
}
}
JavaScript config (lets you require plugins programmatically)
// 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.
| Concern | Owner |
|---|---|
| Indentation, line width, quote style, trailing comma | Prettier |
| Semicolon enforcement (style) | Prettier |
no-unused-vars, no-undef, prefer-const | ESLint |
react-hooks/exhaustive-deps | ESLint |
| Import ordering | ESLint (eslint-plugin-import) or Prettier plugin (@trivago/prettier-plugin-sort-imports) |
Sorting package.json keys | Prettier 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.
// 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:
npx eslint-config-prettier src/app.ts
Output (no conflicts):
No rules that are unnecessary or conflict with Prettier were found.
Output (with conflicts):
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 --fixandprettier --writeboth 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.
npm install -D prettier-plugin-tailwindcss
Output: (none — exits 0 on success)
// .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindFunctions": ["clsx", "cn", "twMerge"]
}
Before:
<div className="text-white p-4 bg-blue-500 hover:bg-blue-700 flex items-center">
After:
<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.
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).
{
"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.
{
"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:
| Concern | Prettier | Biome |
|---|---|---|
| Speed | Single-threaded Node; 1–5 s for 1k files | Multi-threaded Rust; sub-second for 1k files |
| Markdown / YAML / HTML | Yes | No (JS/TS/JSON/CSS/GraphQL only) |
| Plugin ecosystem | Mature (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 style | Double default | Double default |
| Config file | .prettierrc | biome.json |
| Editor support | Universal | VS 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 --fixandprettier --writefight each other — installeslint-config-prettierso 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. .prettierignoredoes not inherit from.gitignore— Prettier 3+ added--ignore-path .gitignoreif you want unified ignores; otherwise list paths explicitly.- Plugin order changes output —
prettier-plugin-tailwindcssmust 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=lfin.gitattributesto avoid format diffs caused by git checkout. prettier-ignorenot respected in HTML — must be the comment immediately before the element, with no intervening blank lines.- Prettier 3 changed
trailingCommadefault to"all"— bumping from v2 reformats every callsite. Either accept the diff in one commit or pintrailingComma: "es5"temporarily. - Cache file checked in — Prettier 3 added
--cache; the cache lives atnode_modules/.cache/prettier/.prettier-cache. Don't commit it.
Real-world recipes
Format only staged files
# 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:
src/app.ts 24ms
src/utils.ts 11ms (unchanged)
Fast incremental formatting with --cache
npx prettier --check --cache --cache-strategy metadata .
Output (warm cache):
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:
dist/**
!dist/important-file.js
Output: (none — exits 0 on success)
Format a single file from stdin (useful for editor integrations)
cat src/app.ts | npx prettier --stdin-filepath src/app.ts
Output:
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
// 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
# .github/workflows/ci.yml
- name: Prettier check
run: npx prettier --check --cache .
For a friendlier failure message:
- name: Prettier check
run: |
if ! npx prettier --check --cache .; then
echo "::error::Run 'npx prettier --write .' locally and commit."
exit 1
fi