Prettier 04 ๐จ The .prettierrc File โ Configuration formats & resolution order
Prettier’s configuration file is deceptively simple. It is an object with a small set of options. But the file that holds that object can take at least a dozen different forms, from a JSON file named .prettierrc to a TypeScript module named prettier.config.ts, to a "prettier" key buried inside package.json. Prettier resolves these in a strict precedence order, and the resolution process starts not at the project root but at the directory of the file being formatted, walking upward until it finds a configuration or reaches the filesystem root.
This chapter covers the configuration formats Prettier accepts, the precedence order when multiple formats coexist, the upward search algorithm that determines which configuration applies to a given file, and the deliberate absence of a global configuration mechanism. It also covers the overrides feature, which lets a single configuration apply different options to different files, and the parser option that must never appear at the top level.
Key point: Prettier does not have a global configuration. It resolves configuration per file, starting from that file’s location and searching upward. The first configuration found wins, and the format of that configuration is determined by a fixed precedence list, not by which file was created most recently.
Why configuration resolution works this way
The reproducibility problem. Prettier intentionally refuses to support global configuration. The documentation states the reason directly: to ensure that when a project is copied to another computer, Prettier’s behavior stays the same . A global configuration would mean that the formatting of a project depends on the machine it is formatted on, not on the project itself. That defeats the purpose of a formatter that guarantees consistent output across a team.
The monorepo problem. A single project might contain multiple packages, each with its own formatting conventions. A per-file, upward-searching resolution algorithm handles this: a file in packages/legacy/ can use a different configuration than a file in packages/modern/, because Prettier finds the nearest configuration first. The resolution does not assume that one configuration applies to an entire repository.
The format proliferation problem. Prettier accepts many configuration formats because different projects have different needs. A simple project can use a JSON .prettierrc. A project that needs computed values or conditional logic can use a JavaScript or TypeScript configuration. A project that already uses TOML for other tooling can use .prettierrc.toml. The precedence order exists to resolve the ambiguity when a project contains more than one format, which happens more often than it should.
a. The configuration formats
Prettier accepts configuration through the following mechanisms, listed in order of precedence :
// 1. A "prettier" key in package.json (highest precedence)
{
"name": "my-project",
"prettier": {
"semi": false,
"singleQuote": true
}
}
# 2. A .prettierrc file written in JSON or YAML
# .prettierrc (YAML is valid here)
semi: false
singleQuote: true
// 3. Explicit-extension JSON or YAML variants
// .prettierrc.json, .prettierrc.yml, .prettierrc.yaml, .prettierrc.json5
{
"semi": false,
"singleQuote": true
}
// 4. JavaScript or TypeScript configurations
// .prettierrc.js, prettier.config.js, .prettierrc.ts, prettier.config.ts
// The module system (ESM or CommonJS) depends on package.json "type"
export default {
semi: false,
singleQuote: true,
};
// 5. Explicit ESM configurations
// .prettierrc.mjs, prettier.config.mjs, .prettierrc.mts, prettier.config.mts
export default {
semi: false,
singleQuote: true,
};
// 6. Explicit CommonJS configurations
// .prettierrc.cjs, prettier.config.cjs, .prettierrc.cts, prettier.config.cts
module.exports = {
semi: false,
singleQuote: true,
};
# 7. TOML configuration (lowest precedence)
# .prettierrc.toml
semi = false
singleQuote = true
The precedence order matters when multiple formats exist in the same directory. A "prettier" key in package.json wins over a .prettierrc file, which wins over .prettierrc.json, and so on. In practice, having more than one format in the same directory is a configuration error, not a feature. The precedence list exists to define behavior in that case, not to encourage it.
b. The resolution algorithm
The configuration file is resolved starting from the location of the file being formatted and searching up the file tree until a configuration file is found . This is not a search for “the project’s configuration”; it is a search for “the nearest configuration to this file.”
Consider a repository with this structure:
repo/
โโโ .prettierrc (semi: true)
โโโ src/
โ โโโ app.js
โโโ packages/
โโโ legacy/
โโโ .prettierrc (semi: false)
โโโ old.js
Formatting src/app.js searches src/, then repo/, finds repo/.prettierrc, and uses semi: true. Formatting packages/legacy/old.js searches packages/legacy/, finds packages/legacy/.prettierrc, and uses semi: false. The inner configuration wins because it is found first. The search never proceeds upward past the first match.
If no configuration is found anywhere in the tree, Prettier uses its built-in defaults. There is no fallback to a global configuration, because Prettier does not support one .
c. Overrides: per-file configuration
A single configuration file can apply different options to different files using the overrides key. Each override entry requires a files pattern, which may be a string or an array of strings. An optional excludeFiles pattern excludes files from that override .
{
"semi": true,
"singleQuote": true,
"overrides": [
{
"files": ["*.json", "*.jsonc"],
"options": {
"trailingComma": "none"
}
},
{
"files": "legacy/**/*.js",
"excludeFiles": "legacy/vendor/**/*.js",
"options": {
"semi": false
}
}
]
}
The files patterns are matched against the file path relative to the directory containing the configuration file, not the current working directory. A pattern without a /, such as *.html, matches against the file name alone and applies at any depth .
d. The parser option and its restriction
The parser option tells Prettier which parser to use. By default, Prettier infers the parser from the file extension. The parser option can be set inside an override to handle files that Prettier does not recognize by extension, but it must never appear at the top level of the configuration.
The documentation is explicit about this: putting parser at the top level disables Prettier’s automatic extension-based inference and forces every file to use the specified parser, including CSS files parsed as JavaScript . This is not a configuration choice; it is a misconfiguration that breaks formatting for every file type the project contains.
Complete Example Session
// ============================================
// PART 1: MINIMAL package.json CONFIGURATION
// ============================================
{
"name": "my-project",
"prettier": {
"semi": false,
"singleQuote": true
}
}
// Highest precedence. Wins over any .prettierrc file
// in the same directory.
# ============================================
# PART 2: .prettierrc AS YAML
# ============================================
# .prettierrc (no extension, YAML content)
semi: false
singleQuote: true
printWidth: 100
// ============================================
// PART 3: .prettierrc.json WITH EXPLICIT EXTENSION
// ============================================
{
"semi": false,
"singleQuote": true,
"printWidth": 100
}
// Lower precedence than .prettierrc (no extension)
// but same directory.
// ============================================
// PART 4: JAVASCRIPT CONFIGURATION (ESM)
// ============================================
// prettier.config.js with "type": "module" in package.json
export default {
semi: false,
singleQuote: true,
overrides: [
{
files: "*.md",
options: { proseWrap: "always" },
},
],
};
// ============================================
// PART 5: COMMONJS CONFIGURATION
// ============================================
// .prettierrc.cjs
module.exports = {
semi: false,
singleQuote: true,
};
// Explicit .cjs extension forces CommonJS regardless
// of package.json "type".
# ============================================
# PART 6: TOML CONFIGURATION
# ============================================
# .prettierrc.toml
semi = false
singleQuote = true
printWidth = 100
// ============================================
// PART 7: OVERRIDES FOR DIFFERENT FILE TYPES
// ============================================
{
"semi": true,
"overrides": [
{
"files": ["*.json", "*.jsonc"],
"options": { "trailingComma": "none" }
},
{
"files": "legacy/**/*.js",
"excludeFiles": "legacy/vendor/**/*.js",
"options": { "semi": false }
}
]
}
// ============================================
// PART 8: PARSER IN AN OVERRIDE (CORRECT)
// ============================================
{
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}
// Teaches Prettier to format its own config file.
// parser is inside overrides, not at the top level.
// ============================================
// PART 9: PARSER AT TOP LEVEL (WRONG)
// ============================================
{
"parser": "babel"
}
// This forces babel for every file type.
// CSS, JSON, and Markdown will fail to parse.
// Never do this.
// ============================================
// PART 10: MONOREPO RESOLUTION
// ============================================
// repo/.prettierrc โ semi: true
// repo/packages/legacy/.prettierrc โ semi: false
//
// Formatting repo/src/app.js:
// Search: repo/src/ โ repo/ โ found (semi: true)
//
// Formatting repo/packages/legacy/old.js:
// Search: repo/packages/legacy/ โ found (semi: false)
// Inner config wins; search stops at first match.
The ten parts covered the package.json key, YAML .prettierrc, explicit JSON extension, ESM JavaScript, CommonJS, TOML, overrides, parser inside overrides, the parser top-level error, and monorepo resolution behavior.
Quick Reference
Configuration precedence (highest to lowest)
| Order | Format |
|---|---|
| 1 | "prettier" key in package.json |
| 2 | .prettierrc (JSON or YAML, no extension) |
| 3 | .prettierrc.json, .prettierrc.yml, .prettierrc.yaml, .prettierrc.json5 |
| 4 | .prettierrc.js, prettier.config.js, .prettierrc.ts, prettier.config.ts |
| 5 | .prettierrc.mjs, prettier.config.mjs, .prettierrc.mts, prettier.config.mts |
| 6 | .prettierrc.cjs, prettier.config.cjs, .prettierrc.cts, prettier.config.cts |
| 7 | .prettierrc.toml |
Resolution behavior
| Behavior | Detail |
|---|---|
| Search start | Directory of the file being formatted |
| Search direction | Upward through the file tree |
| Search stop | First configuration found |
| Global configuration | Not supported |
| No configuration found | Built-in defaults apply |
Overrides keys
| Key | Required | Purpose |
|---|---|---|
files | Yes | Glob(s) matching files for this override |
excludeFiles | No | Glob(s) excluding files from this override |
options | Yes | Prettier options to apply |
Best Practices
โ Do This:
// Use one configuration format per directory
{ "semi": false, "singleQuote": true } // โ
// Use overrides for file-type-specific options
{ "overrides": [{ "files": "*.md", "options": { "proseWrap": "always" } }] } // โ
// Put parser only inside overrides
{ "overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }] } // โ
// Use JavaScript config when computed values are needed
export default { semi: !process.env.CI }; // โ
# Verify which config applies to a file
npx prettier --find-config-path src/app.js # โ
โ Don’t Do This:
// Put parser at the top level
{ "parser": "babel" } // โ
// Create multiple config formats in the same directory
// (.prettierrc and .prettierrc.json together) // โ
# Expect a global config to apply
prettier --write . # โ (no global config)
// Assume the root config applies everywhere in a monorepo
// Inner configs win; the root config is only for files
// not covered by a nearer config // โ
// Use .prettierrc with JSON content but no extension
// (it works, but the format is ambiguous) // โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Formatting differs between directories | A nearer config overrides the root config | Check --find-config-path for each file |
| CSS files fail to parse | parser at top level forces JavaScript parser | Remove top-level parser; use overrides if needed |
| Config changes have no effect | A higher-precedence format exists in the same directory | Remove duplicate config formats |
| Monorepo package formats differently | Inner config found before root config | Accept the behavior; that is the design |
.prettierrc not recognized | File has invalid JSON or YAML syntax | Validate syntax; use explicit extension |
Real-World Examples
1. Minimal package.json config
{ "prettier": { "semi": false, "singleQuote": true } }
2. Empty YAML .prettierrc
# .prettierrc
{}
3. JavaScript config with overrides
export default {
semi: false,
overrides: [{ files: "*.md", options: { proseWrap: "always" } }],
};
4. CommonJS config
module.exports = { semi: false, singleQuote: true };
5. TOML config
semi = false
singleQuote = true
6. Override for JSON files
{ "overrides": [{ "files": "*.json", "options": { "trailingComma": "none" } }] }
7. Override with excludeFiles
{ "overrides": [{ "files": "legacy/**/*.js", "excludeFiles": "legacy/vendor/**", "options": { "semi": false } }] }
8. Parser inside override
{ "overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }] }
9. Find config for a file
npx prettier --find-config-path src/app.js
10. Monorepo with inner config
# repo/.prettierrc
# repo/packages/legacy/.prettierrc
# legacy files use the inner config
Visual
Configuration precedence per directory
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SAME DIRECTORY: WHICH CONFIG WINS? โ
โ โ
โ package.json "prettier" key โ highest โ
โ โ โ
โ โผ โ
โ .prettierrc (JSON or YAML) โ
โ โ โ
โ โผ โ
โ .prettierrc.json / .yml / .yaml / .json5 โ
โ โ โ
โ โผ โ
โ .prettierrc.js / prettier.config.js / .ts โ
โ โ โ
โ โผ โ
โ .prettierrc.mjs / .mts โ
โ โ โ
โ โผ โ
โ .prettierrc.cjs / .cts โ
โ โ โ
โ โผ โ
โ .prettierrc.toml โ lowest โ
โ โ
โ Only the highest-precedence format is used. โ
โ Lower-precedence formats in the same directory are ignored. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Upward search resolution
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ RESOLUTION FOR packages/legacy/old.js โ
โ โ
โ Start: packages/legacy/ โ
โ โ โ
โ โผ โ
โ Check for config in packages/legacy/ โ
โ โ โ
โ โโโ Found .prettierrc โโโบ Use it, STOP โ
โ โ โ
โ โโโ Not found โโโบ Go up one directory โ
โ โ โ
โ โผ โ
โ Check packages/ โ
โ โ โ
โ โโโ Found โโโบ Use it, STOP โ
โ โ โ
โ โโโ Not found โโโบ Go up โ
โ โ โ
โ โผ โ
โ Check repo/ โ
โ โ โ
โ โโโ Found โโโบ Useโ
โ โ โ
โ โโโ Not found โโโบโ
โ Defaults โ
โ โ
โ The first match wins. The search never continues upward โ
โ after a config is found. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Monorepo with nested configs
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MONOREPO STRUCTURE โ
โ โ
โ repo/ โ
โ โโโ .prettierrc (semi: true) โ
โ โโโ src/ โ
โ โ โโโ app.js โ uses repo/.prettierrc โ
โ โ โ
โ โโโ packages/ โ
โ โโโ modern/ โ
โ โ โโโ new.js โ uses repo/.prettierrc โ
โ โ โ
โ โโโ legacy/ โ
โ โโโ .prettierrc (semi: false) โ
โ โโโ old.js โ uses legacy/.prettierrc โ
โ โ
โ legacy/old.js finds the legacy config first. โ
โ src/app.js and modern/new.js walk up to repo/.prettierrc. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Overrides processing
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ OVERRIDES PROCESSING โ
โ โ
โ Base options from config: โ
โ semi: true โ
โ singleQuote: true โ
โ โ โ
โ โผ โ
โ File being formatted: legacy/old.js โ
โ โ โ
โ โผ โ
โ Check overrides in order: โ
โ override 1: files: "*.json" โโโบ no match โ
โ override 2: files: "legacy/**/*.js" โโโบ match โ
โ โ โ
โ โผ โ
โ Merge override options onto base: โ
โ semi: false (from override) โ
โ singleQuote: true (from base, not overridden) โ
โ โ โ
โ โผ โ
โ Final options for legacy/old.js โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Highest precedence | "prettier" key in package.json |
| Lowest precedence | .prettierrc.toml |
| Resolution start | Directory of the file being formatted |
| Resolution direction | Upward, first match wins |
| Global configuration | Not supported |
| Overrides key | overrides, with files, excludeFiles, options |
| Parser restriction | Only inside overrides, never at top level |
| Search verification | prettier --find-config-path <file> |
Key takeaways:
- Configuration is resolved per file, not per project. The search starts at the file’s directory and stops at the first configuration found. Inner configurations override outer ones by being found first.
- The precedence order resolves format ambiguity. If multiple configuration formats exist in the same directory,
package.jsonwins, then extensionless.prettierrc, then explicit extensions, then JavaScript variants, then TOML. - Global configuration is deliberately absent. Prettier refuses to support it because a global config makes formatting depend on the machine, not the project.
overridesapplies different options to different files within one configuration. Thefilespattern is matched relative to the configuration file’s directory, not the current working directory.parserat the top level breaks everything. It disables extension-based inference and forces one parser on every file type. Use it only insideoverrides.- Monorepos work because of the upward search. A package can have its own configuration, and files within that package use it without affecting files in other packages.
Remember: The .prettierrc file is not a project-level setting. It is a node in a per-file lookup chain. A file’s formatting is determined by the nearest configuration above it, and that configuration’s format is determined by the precedence list, not by which file was created first. This design exists to make formatting reproducible across machines and predictable across monorepos. The two mistakes that cause the most confusion are expecting a root configuration to apply everywhere (it does not, if a nearer configuration exists) and putting parser at the top level (which forces every file through one parser). Keep configuration minimal, keep one format per directory, and use --find-config-path when the output does not match expectations.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!