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

bash
# 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.

bash
# 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].

bash
# 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, default trailingComma: "all"). 2.x predates ESM-only.
  • Requires Node 14+ (3.x). New major lines bump the Node floor every few years.
  • ESM-only since 3.0require("prettier") no longer works in modern Node. CJS consumers must use dynamic import("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:

PluginPurpose
prettier-plugin-tailwindcssSort 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-phpOfficial PHP.
@prettier/plugin-xmlOfficial XML.
prettier-plugin-organize-importsSort + remove unused imports (TS only).
prettier-plugin-prismaschema.prisma support.
prettier-plugin-sql / prettier-plugin-shSQL 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

ToolTrade-off
@biomejs/biomeRust-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.
dprintRust formatter, plugin-based, very fast. Smaller ecosystem; great for monorepos that want explicit plugin control.
eslint --fix with stylistic rulesPre-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 fmtBundled with Deno. Comparable to Prettier; tied to Deno.
no formatterDon't. Style debates eat more time than the install.

Common gotchas

  1. 3.0 is ESM-only. Old CJS consumers (require("prettier") in IDE plugins, custom scripts, older lint-staged versions) break at upgrade time. Fix by either pinning Prettier 2.x or migrating callers to dynamic await import("prettier").
  2. 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.json listing every plugin you actually use.
  3. printWidth default 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.
  4. .prettierignore is not .gitignore. Patterns are similar but not identical (no negation re-include behaviour after a directory exclude). Always run prettier --check . once after editing the ignore file to confirm.
  5. --ignore-path shadows defaults. Passing --ignore-path .gitignore replaces .prettierignore, not stacks with it. Use --ignore-path .gitignore --ignore-path .prettierignore (multiple, repeatable) on 3.x.
  6. 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-prettier so it's a single pass. Otherwise the file flickers and git's inode watcher sometimes misses the second write.
  7. Plugin ordering matters. When using multiple plugins (e.g. Astro + Tailwind), Tailwind's plugin must come after language plugins in the plugins array — 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:

json
{
  "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`:

json
{ "embeddedLanguageFormatting": "auto" }

Set to "off" if you want SQL inside template literals to stay as-is.

Pre-commit via lint-staged

json
{
  "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":

bash
npm install -D --save-exact prettier

Output:

text
added 1 package, and audited 178 packages in 2s

CI as a gate

bash
# Exits 1 if any file isn't formatted
npx prettier --check .

Output:

text
Checking formatting...
[warn] src/utils.ts
[warn] Code style issues found in 1 file. Run Prettier with --write to fix.

In GitHub Actions:

yaml
- 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:

json
{
  "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

yaml
# .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:

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+)

bash
npx prettier --write --cache .

Output:

text
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:

bash
npx prettier --write "src/**/*.{ts,tsx,css,json}"

Output:

text
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 dynamic await import("prettier").
  • lint-staged and husky updated to handle ESM.
  • Custom scripts that loaded Prettier programmatically need to be .mjs or 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.

typescript
// Modern programmatic use
import prettier from "prettier";
const result = await prettier.format(source, { parser: "typescript" });

Version migration guide

From → ToHighlights
1.x → 2.0Massive whitespace changes (one-line objects became multi-line in many cases). Land on a single commit.
2.x → 3.0ESM-only. trailingComma default changed from "es5" to "all". singleAttributePerLine for HTML. --ignore-path accepts multiple values. Plugins must export ESM.
3.0 → 3.3Performance: --cache flag. New singleAttributePerLine Vue support.
3.3 → 3.6TypeScript 5.5 syntax support. Bug fixes for JSX. Minor formatter tweaks.

Common 2→3 friction

  • Old lint-staged (<13.2) calls Prettier with require — upgrade.
  • eslint-plugin-prettier older versions can't load ESM Prettier — upgrade to 5.x.
  • Husky scripts that source node_modules/prettier/index.js are broken — switch to invoking the bin or use await 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

  1. 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.
  2. CI runs Prettier with repo write access. Don't --write in CI on pull_request_target workflows — that's a code-injection vector. Use --check in CI; only --write in pre-commit or local.
  3. Editor integration spawns the binary. VS Code's Prettier extension shells out to node_modules/.bin/prettier. A compromised dep can replace this; npm ci from a verified lockfile mitigates.
  4. .prettierignore mistakes leak diffs. Forgetting to ignore generated files can re-format them on every save, creating noisy diffs. Audit .prettierignore periodically.

Configuration patterns

Tabs vs spaces

json
{ "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

json
{ "singleQuote": true, "jsxSingleQuote": false }

A common pairing: single quotes in JS, double quotes in JSX (matches HTML convention).

Per-language overrides

json
{
  "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:

json
// .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

text
# 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 to plugins array in config.
  • Tailwind classes not sorted — plugin ordering. Tailwind plugin must be last in plugins.
  • Error: ENOENT: no such file or directory, open '...'.prettierignore doesn't match what you expect. Run prettier --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 token in a config file — .prettierrc.js is now CJS or ESM depending on package.json "type". Use .prettierrc.cjs for explicit CJS or .prettierrc.mjs for explicit ESM.

Ecosystem integrations

Beyond the plugins listed in the install section:

ToolWhat it adds
eslint-config-prettierDisables ESLint stylistic rules that conflict with Prettier — the preferred bridge
eslint-plugin-prettierRuns Prettier inside ESLint as a rule (slower; only use when you need a single tool)
prettier-plugin-tailwindcssSort Tailwind classes; the de-facto plugin
prettier-plugin-astro.astro files (Astro components)
prettier-plugin-svelte.svelte files
prettier-plugin-organize-importsSort + remove unused TS imports
@prettier/plugin-phpFirst-party PHP
prettier-plugin-prisma.prisma schema files
prettier-plugin-sql / …-shSQL + shell
Editor: Prettier VS Code, IntelliJ, Sublime, AtomAll 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 pgFormatter or sqlfluff directly.

See also