cheat sheet
prettier
Package-level reference for prettier on npm — install variants, v3 ESM-only switch, plugin model (Tailwind, Astro, etc.), Node support, and alternatives.
prettier
What it is
prettier is an opinionated code formatter created by James Long that re-prints code from its own AST — your formatting choices are reduced to a handful of options (printWidth, tabWidth, singleQuote, trailingComma, semi). Everything else is fixed, which is the point: the global format becomes consensus, and code review stops arguing about style.
It supports JS, TS, JSX, JSON, CSS/SCSS/Less, HTML, Vue, Angular, Markdown, MDX, YAML, and GraphQL out of the box, with first-party and community plugins for everything from Tailwind class sorting to Astro components to PHP.
Install
# Always a devDep — Prettier is a build-time tool
npm install -D prettier
pnpm add -D prettier
yarn add -D prettier
bun add -d prettier
Output: prettier binary on PATH under node_modules/.bin/prettier.
# Day-to-day
npx prettier --check . # CI: exit non-zero on any unformatted file
npx prettier --write . # format in place
npx prettier --write "src/**/*.ts" # restrict to a glob
Output: Checked N files in <ms> or per-file [written|unchanged].
# A typical plugin
npm install -D prettier-plugin-tailwindcss # sort Tailwind classes
npm install -D prettier-plugin-astro # .astro files
npm install -D @prettier/plugin-php # PHP support (scoped)
Output: plugins auto-discovered if listed in package.json; opt in explicitly via "plugins" in .prettierrc.json if pnpm's isolated layout hides them.
Versioning & Node support
- Current major line is
3.x(released 2023 — landed ESM-only, plugin-API rewrite, defaulttrailingComma: "all").2.xpredates ESM-only. - Requires Node 14+ (
3.x). New major lines bump the Node floor every few years. - ESM-only since
3.0—require("prettier")no longer works in modern Node. CJS consumers must use dynamicimport("prettier"). This broke a long tail of tooling (lint-staged, husky scripts, IDE plugins) at the transition. - Always a dev dependency — Prettier doesn't run at app runtime.
- Strict semver: rule defaults change only on major bumps; minors add language/syntax support; patches fix bugs.
Package metadata
- Maintainer: Prettier team (community / Vercel-sponsored core)
- Project home: github.com/prettier/prettier
- Docs: prettier.io/docs
- npm: npmjs.com/package/prettier
- License: MIT
- First released: 2017
- Downloads: tens of millions per week — one of the most-downloaded packages on npm.
Peer dependencies & extras
prettier itself has no peer deps. The ecosystem extends through plugins, all of which are normal devDeps:
| Plugin | Purpose |
|---|---|
prettier-plugin-tailwindcss | Sort Tailwind class names — official Tailwind Labs plugin, almost universally adopted. |
prettier-plugin-astro | .astro component support. |
prettier-plugin-svelte | .svelte support — distinct from the bundled Vue/Angular plugins. |
@prettier/plugin-php | Official PHP. |
@prettier/plugin-xml | Official XML. |
prettier-plugin-organize-imports | Sort + remove unused imports (TS only). |
prettier-plugin-prisma | schema.prisma support. |
prettier-plugin-sql / prettier-plugin-sh | SQL and shell scripts. |
Plugins listed in package.json are normally auto-discovered. In pnpm's strict layout they aren't — list them explicitly under "plugins": ["prettier-plugin-tailwindcss"] in .prettierrc.json.
eslint-config-prettier and eslint-plugin-prettier are the bridge to ESLint — the former turns off ESLint rules that conflict with Prettier; the latter runs Prettier as an ESLint rule. Prefer the config-only approach (faster, fewer false positives).
Alternatives
| Tool | Trade-off |
|---|---|
| @biomejs/biome | Rust-based, ~25–35× faster, formatter + linter in one. Coverage is ~95% of Prettier; missing some plugins (notably perfect Tailwind sorting parity, MDX). Pick for new projects without long Prettier plugin lists. |
| dprint | Rust formatter, plugin-based, very fast. Smaller ecosystem; great for monorepos that want explicit plugin control. |
| eslint --fix with stylistic rules | Pre-Prettier approach. Slower, far less consistent, and the JS community has mostly moved on. @stylistic/eslint-plugin revives this for teams that want one tool. |
| deno fmt | Bundled with Deno. Comparable to Prettier; tied to Deno. |
| no formatter | Don't. Style debates eat more time than the install. |
Common gotchas
3.0is ESM-only. Old CJS consumers (require("prettier")in IDE plugins, custom scripts, olderlint-stagedversions) break at upgrade time. Fix by either pinning Prettier2.xor migrating callers to dynamicawait import("prettier").- Plugin resolution in pnpm. pnpm doesn't hoist transitive packages, so Prettier's plugin auto-discovery misses them. Add an explicit
"plugins": [...]array in.prettierrc.jsonlisting every plugin you actually use. printWidthdefault 80 vs 100 debate. Prettier's default is 80. Many teams override to 100 or 120. Whichever you pick, do it explicitly in config and pre-commit the codebase before code review — otherwise every PR carries hundreds of churn-only diffs..prettierignoreis not.gitignore. Patterns are similar but not identical (no negation re-include behaviour after a directory exclude). Always runprettier --check .once after editing the ignore file to confirm.--ignore-pathshadows defaults. Passing--ignore-path .gitignorereplaces.prettierignore, not stacks with it. Use--ignore-path .gitignore --ignore-path .prettierignore(multiple, repeatable) on3.x.- Editor integration writes file twice if you chain ESLint. Configure either "Prettier on save + ESLint fix on save" with explicit ordering, or unify with
eslint-plugin-prettierso it's a single pass. Otherwise the file flickers and git'sinodewatcher sometimes misses the second write. - Plugin ordering matters. When using multiple plugins (e.g. Astro + Tailwind), Tailwind's plugin must come after language plugins in the
pluginsarray — otherwise class sorting doesn't trigger inside Astro/Svelte templates.
Real-world recipes
The patterns that come up on real codebases — plugin ordering, multi-file types, and CI/pre-commit integration.
Plugin ordering with Tailwind
The Tailwind class-sort plugin must come after any language plugin so it can hook into the parsed templates. The convention is to list it last:
{
"plugins": [
"prettier-plugin-astro",
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
]
}
In pnpm the order is doubly important — pnpm doesn't hoist plugins, so without an explicit list they aren't discovered.
Embedded language formatting
Prettier formats code blocks inside Markdown, GraphQL inside gql\`template tags, and HTML inside lit-html. Toggle withembeddedLanguageFormatting`:
{ "embeddedLanguageFormatting": "auto" }
Set to "off" if you want SQL inside template literals to stay as-is.
Pre-commit via lint-staged
{
"lint-staged": {
"*.{js,jsx,ts,tsx,json,md,css}": ["prettier --write"],
"*.{js,jsx,ts,tsx}": ["eslint --fix"]
}
}
Order matters: Prettier runs first (re-prints code), ESLint runs second (catches semantic issues on the formatted output). Reverse and you'll re-print over ESLint's autofixes.
Pin exact version
Prettier patches sometimes nudge whitespace. Pin exact to avoid "CI failed because someone's lockfile drifted":
npm install -D --save-exact prettier
Output:
added 1 package, and audited 178 packages in 2s
CI as a gate
# Exits 1 if any file isn't formatted
npx prettier --check .
Output:
Checking formatting...
[warn] src/utils.ts
[warn] Code style issues found in 1 file. Run Prettier with --write to fix.
In GitHub Actions:
- run: npx prettier --check .
Pair with eslint . (no --fix) and a CI job named lint that both must pass.
Multi-config monorepo
The simplest pattern: one root .prettierrc extended by overrides per package via overrides:
{
"semi": false,
"singleQuote": true,
"overrides": [
{ "files": "packages/legacy/**", "options": { "semi": true } },
{ "files": "*.md", "options": { "proseWrap": "always" } }
]
}
Each package can override with its own .prettierrc that Prettier searches up the directory tree.
Production deployment
Prettier is a dev-time tool with no runtime. The deployment concern is purely "is CI green?"
CI gate
# .github/workflows/format.yml
- run: npx prettier --check .
--check exits non-zero on any unformatted file, prints the list, doesn't write. The matching --write is the local fix.
Lock the version
package.json:
{ "devDependencies": { "prettier": "3.6.2" } }
No caret — patches can change output. Use Renovate / Dependabot to bump intentionally.
Pre-merge enforcement
Combine with GitHub branch protection: require the format check to pass before merge. Combine with the commit-status action so a "format" status appears on every PR.
Performance tuning
Prettier is fast enough for most codebases (1-3s on ~1000 files), but a few knobs help on huge repos.
--cache (v3.0+)
npx prettier --write --cache .
Output:
src/utils.ts 42ms
(unchanged) src/index.ts 8ms
(unchanged) src/App.tsx 6ms
Stores hashes in node_modules/.cache/prettier/.prettier-cache. Subsequent runs skip unchanged files. 5-20× speedup on warm cache.
Scope the glob
prettier --write . is convenient but walks the whole tree (including node_modules if .prettierignore is incomplete). Be explicit:
npx prettier --write "src/**/*.{ts,tsx,css,json}"
Output:
src/components/Button.tsx 31ms
src/styles/main.css 12ms
src/data/config.json 4ms
Workers via lint-staged
lint-staged shards staged files into parallel prettier --write invocations. On a 5,000-file commit, this is 3-4× faster than a single invocation.
vs. Biome / dprint
Prettier is single-threaded Node. Biome is Rust + parallel; dprint is Rust + plugin-parallel. Wins:
- Prettier ~1000 files cold: ~2-3 s
- Biome same files: ~80-150 ms
- dprint same files: ~150-300 ms
The wall-clock difference shows up in pre-commit hooks and watch-mode IDE integration. For CI the gap is smaller (download cost amortised).
ESM/CJS interop & bundling
Prettier 3.0+ is ESM-only. The breakage at the transition was real:
- IDE plugins that did
require("prettier")had to switch to dynamicawait import("prettier"). lint-stagedandhuskyupdated to handle ESM.- Custom scripts that loaded Prettier programmatically need to be
.mjsor in an ESM package.
The CLI is unaffected — npx prettier works identically. Only programmatic callers care about ESM vs CJS.
Plugins follow the same constraint: a Prettier 3.x plugin must export ESM. The community has mostly migrated; a few abandoned plugins still ship CJS and are incompatible.
// Modern programmatic use
import prettier from "prettier";
const result = await prettier.format(source, { parser: "typescript" });
Version migration guide
| From → To | Highlights |
|---|---|
| 1.x → 2.0 | Massive whitespace changes (one-line objects became multi-line in many cases). Land on a single commit. |
| 2.x → 3.0 | ESM-only. trailingComma default changed from "es5" to "all". singleAttributePerLine for HTML. --ignore-path accepts multiple values. Plugins must export ESM. |
| 3.0 → 3.3 | Performance: --cache flag. New singleAttributePerLine Vue support. |
| 3.3 → 3.6 | TypeScript 5.5 syntax support. Bug fixes for JSX. Minor formatter tweaks. |
Common 2→3 friction
- Old
lint-staged(<13.2) calls Prettier withrequire— upgrade. eslint-plugin-prettierolder versions can't load ESM Prettier — upgrade to5.x.- Husky scripts that source
node_modules/prettier/index.jsare broken — switch to invoking the bin or useawait import.
The trailingComma: "all" default change adds a comma after the last function parameter and last array element in many positions. Land that diff in one mechanical commit; it dwarfs every other 2→3 churn.
Security considerations
- Plugin install = code execution. A malicious Prettier plugin can run arbitrary code during
prettier --write(it has full Node access). Audit plugins from unknown authors; pin exact versions. - CI runs Prettier with repo write access. Don't
--writein CI onpull_request_targetworkflows — that's a code-injection vector. Use--checkin CI; only--writein pre-commit or local. - Editor integration spawns the binary. VS Code's Prettier extension shells out to
node_modules/.bin/prettier. A compromised dep can replace this;npm cifrom a verified lockfile mitigates. .prettierignoremistakes leak diffs. Forgetting to ignore generated files can re-format them on every save, creating noisy diffs. Audit.prettierignoreperiodically.
Configuration patterns
Tabs vs spaces
{ "useTabs": true, "tabWidth": 2 }
Tab + width-2 looks 2-space wide; accessible to readers who configure their tab width differently. The defaults are spaces — pick once, project-wide.
Quote consistency
{ "singleQuote": true, "jsxSingleQuote": false }
A common pairing: single quotes in JS, double quotes in JSX (matches HTML convention).
Per-language overrides
{
"overrides": [
{ "files": "*.md", "options": { "proseWrap": "always", "printWidth": 80 } },
{ "files": "*.{yml,yaml}", "options": { "tabWidth": 2 } }
]
}
Editor integration without auto-format-on-save
If your team prefers explicit formatting (no on-save), bind a keystroke instead. VS Code:
// .vscode/settings.json
{ "editor.formatOnSave": false }
…and developers run Cmd/Ctrl+Shift+P → Format Document when ready. Avoids the "two saves, two diffs" pattern that on-save can produce when chained with ESLint.
.prettierignore patterns
# Build artefacts
dist/
build/
.next/
# Generated
*.generated.ts
prisma/migrations/
# Locks
package-lock.json
pnpm-lock.yaml
yarn.lock
# Vendored
public/vendor/
Patterns follow gitignore syntax but without re-include after a directory exclude (a known limitation — use --ignore-path .gitignore to stack with .prettierignore).
Troubleshooting common errors
Cannot find module "prettier-plugin-tailwindcss"— pnpm strict layout. Add topluginsarray in config.- Tailwind classes not sorted — plugin ordering. Tailwind plugin must be last in
plugins. Error: ENOENT: no such file or directory, open '...'—.prettierignoredoesn't match what you expect. Runprettier --debug-check .to see what Prettier thinks the patterns expand to.- CI says "Code style issues found" but local says clean — version drift (CI installs latest, local pinned). Pin exact in
package.json. SyntaxError: Unexpected tokenin a config file —.prettierrc.jsis now CJS or ESM depending onpackage.json"type". Use.prettierrc.cjsfor explicit CJS or.prettierrc.mjsfor explicit ESM.
Ecosystem integrations
Beyond the plugins listed in the install section:
| Tool | What it adds |
|---|---|
eslint-config-prettier | Disables ESLint stylistic rules that conflict with Prettier — the preferred bridge |
eslint-plugin-prettier | Runs Prettier inside ESLint as a rule (slower; only use when you need a single tool) |
prettier-plugin-tailwindcss | Sort Tailwind classes; the de-facto plugin |
prettier-plugin-astro | .astro files (Astro components) |
prettier-plugin-svelte | .svelte files |
prettier-plugin-organize-imports | Sort + remove unused TS imports |
@prettier/plugin-php | First-party PHP |
prettier-plugin-prisma | .prisma schema files |
prettier-plugin-sql / …-sh | SQL + shell |
| Editor: Prettier VS Code, IntelliJ, Sublime, Atom | All shell out to the installed binary |
When NOT to use this
Prettier is the safest choice for "team agrees on formatting" — but a few cases push elsewhere:
- New projects, no Prettier history. Biome is faster, single-binary, includes lint. Strong default for greenfield.
- dprint preference. Rust formatter with explicit plugin model. Pick for monorepos that want per-plugin control.
- Code style debates. If your team can't agree on
singleQuote, pick Biome (more opinionated) or just adopt Prettier's defaults — the meta-debate eats more time than the install. - No Node.js. Prettier requires Node. For Node-less environments, Biome's standalone binary is the alternative.
- Embedded SQL / GraphQL formatting must be sophisticated. Prettier's embedded language formatting is good but not bulletproof — for serious SQL formatting, use
pgFormatterorsqlfluffdirectly.
See also
- JavaScript: prettier — config recipes, integration
- JavaScript: eslint — companion linter
- JavaScript: biome — the modern challenger
- Packages: npm-biome
- Packages: npm-eslint