Prettier Deep Dive: From AST to Enterprise Engineering Practices, Why Teams Choose It
Introduction
If you have ever participated in team development, you have likely experienced this scenario: during a Code Review, the comments section lists a long string of modification suggestions unrelated to business logic.
const name="Tom"// Change toconst name = "Tom";
if(condition){// Change toif (condition) {
const arr = [1,2,3]// Change toconst arr = [1, 2, 3];These changes do not affect any business logic, yet they bring tangible costs: Git Diff expands, Code Review time stretches, team members argue endlessly over “whether to use single or double quotes,” and the codebase style becomes cluttered over time.
Prettier emerged to put an end to this meaningless overhead.
1. What is the Debate Over Code Styles Really About?
Many teams initially try to solve the problem by writing “guidelines”: unified use of single quotes, two-space indentation, semicolons at the end of statements, trailing commas, and writing these rules in a team Wiki.
The problem is, guidelines are soft constraints, and humans are unreliable enforcers. Someone will always forget, someone will use a different editor or default configuration, and someone will copy-paste legacy code with a completely different style. Over time, the project will look like this:
src├── user.js├── order.js├── product.js└── payment.jsEach file has a different style, and even a single file can contain contradictions:
const name = "Tom"const age=18;const city='Shanghai'This is the core of the issue—style consistency is bound to fail if it relies on self-discipline. What Wiki documents cannot solve, tools must solve.
2. Prettier’s Design Philosophy: Removing “Style” from the Discussion
Prettier defines itself as an Opinionated Code Formatter. Its goal is straightforward: stop discussing code formatting, and delegate the decision-making to the tool.
The keyword here is “opinionated.” Many formatting or linting tools offer an abundance of configuration options, allowing teams to customize rules to their preferences; Prettier does the opposite. While it keeps a core set of options such as printWidth, tabWidth, semi, singleQuote, trailingComma, and arrowParens, it deliberately blocks configurations for purely aesthetic details, such as whether braces should wrap or not. For example:
if (){}Prettier will always format it as:
if () {}You cannot change this behavior through configuration. The Prettier team is exceptionally cautious about adding new options. According to their public Option Philosophy, new options are only considered when there is a real technical necessity (e.g., compatibility with a certain syntax ecosystem), whereas purely aesthetic preferences are rejected. Options like objectWrap and experimentalOperatorPosition were only added after long discussions and verification of true necessity, rather than simply being added on demand.
Reducing choice is fundamentally about reducing debate and increasing efficiency—which is the core difference between Prettier and traditional Lint-based style rules.
3. What Prettier Actually Does: Parser → AST → Printer
Many people mistake Prettier for a simple “search and replace” tool. In reality, Prettier’s core is a Parser + AST + Printer system.
1. From Source Code to AST
Suppose we have a line of code:
const sum=(a,b)=>a+bPrettier’s first step is to parse it into an Abstract Syntax Tree (AST):
graph TD A[Source Code] --> B[Parser] B --> C[AST]
The resulting AST has a structure roughly like this (simplified):
{ "type": "VariableDeclaration", "kind": "const", "declarations": [ { "type": "VariableDeclarator" } ]}Notice that during parsing, surface-level details like spaces, newlines, indentation, and single/double quotes are discarded, leaving only the structural relationships of the code.
2. What is an AST?
AST stands for Abstract Syntax Tree. For example, the expression 1 + 2 * 3 corresponds to the following structure:
graph TD A["+"] --> B["1"] A --> C["*"] C --> D["2"] C --> E["3"]
This stores the structural relationship between expressions rather than the raw text—which is why Prettier’s output remains completely stable.
3. Printer: Re-Printing the AST into Code
Once the AST is generated, Prettier “prints” it back out based on a set of unified rules:
graph TD A[AST] --> B[Printer] B --> C[Formatted Code]
Input: const sum=(a,b)=>a+b
Output:
const sum = (a, b) => a + bThe entire pipeline is:
graph TD A[Source Code] --> B[Parser] B --> C[AST] C --> D[Printer] D --> E[Formatted Code]
4. Why Prettier’s Output is Always Stable
Because it doesn’t care what the original code looks like. Whether it is:
const a=1or:
const a = 1;The parsed AST is exactly the same, so the Printer will always output the exact same result—const a = 1;. This “structure-focused, input-agnostic” design makes Prettier naturally idempotent: formatting the same code repeatedly yields the identical result.
5. Different Languages, Different Parsers
Prettier does not use a single generic parser for all languages. Its architecture is a combination of a “core engine + language-specific plugins.” JavaScript/TypeScript/Flow use Babel or TypeScript’s own parser, CSS/Less/SCSS run on PostCSS, Markdown goes through remark, YAML uses a dedicated YAML parser, and HTML, GraphQL, and Angular templates each have their own Parser and Printer implementations. The languages supported natively by Prettier are implemented via this plugin API, packaged and released with the core package so they feel built-in. Understanding this helps explain how Prettier extends to new languages via community plugins.
4. Which Languages Does Prettier Support?
According to Prettier’s official documentation, the core package supports:
- JavaScript (including experimental syntax)
- JSX / TypeScript / Flow
- Vue and Angular templates
- CSS, Less, SCSS
- HTML
- Ember / Handlebars
- JSON (including variants like JSON5 and JSONC)
- GraphQL
- Markdown (including GFM and MDX v1)
- YAML
Languages like Astro, XML, PHP, Ruby, and Swift are supported via separately maintained official or community plugins and are not built into the core package. You must install packages like prettier-plugin-astro or @prettier/plugin-xml separately and register them in your configuration, as detailed in the “Plugin Ecosystem” section.
5. Why is Prettier So Popular?
The real reason is not the formatting itself, but its byproduct—cleaner Git Diffs.
Without Prettier, a commit that only renames a variable might look like this:
const name="Tom"const name = "Tom";The commit is filled with formatting noise, burying the actual logical changes. With Prettier, the diff only highlights meaningful changes:
return oldPrice;return discountedPrice;Consequently, the focus of Code Reviews shifts from “should there be a semicolon here” to “is this business logic correct”—and this is where Prettier truly changes the game.
6. Prettier and ESLint: Cooperation Over Competition
This is one of the classic questions in the frontend domain: who is responsible for what?
ESLint handles Code Quality: detecting unused variables (no-unused-vars), recommending === over ==, which are issues that affect program correctness. Prettier handles Code Formatting: spaces, indentation, newlines, quotes—purely visual unification without logical judgment.
graph TD A[ESLint] --> B[Code Quality: Will it bug out?] C[Prettier] --> D[Code Formatting: Is it uniform?]
Remember this: ESLint ensures code is correct; Prettier ensures code is uniform.
Why Did They Conflict Historically?
Early versions of ESLint included formatting rules like indent, quotes, and semi. If a project ran both ESLint and Prettier, they would easily issue conflicting suggestions on the same code, leading to a loop of formatting and re-formatting.
Modern Solutions
The correct approach is to use eslint-config-prettier to turn off all formatting rules in ESLint that could conflict with Prettier, allowing ESLint to focus on quality and Prettier on formatting.
If your project still uses the older .eslintrc format, the configuration looks like this:
{ "extends": ["some-other-config", "prettier"]}However, if you are using the default Flat Config (eslint.config.js) introduced in ESLint 9, the configuration is different—Flat Config does not use the extends field. Instead, you directly add the configuration object from eslint-config-prettier to the configuration array, placing it after other configurations so it can override conflicting rules:
import js from '@eslint/js'import eslintConfigPrettier from 'eslint-config-prettier/flat'
export default [ js.configs.recommended, // Your other configs (react, typescript-eslint, etc.) go here eslintConfigPrettier, // Must be placed last to turn off conflicting rules]An Easily Overlooked Detail: Avoid eslint-plugin-prettier
Another popular approach in the past was to install eslint-plugin-prettier to wrap Prettier as an ESLint rule (prettier/prettier), displaying formatting issues as ESLint errors. This is no longer recommended today—it slows down ESLint execution and makes error reports less intuitive. The mainstream approach now is to run the two tools independently: prettier --write for formatting, eslint --fix for quality, and let eslint-config-prettier handle the conflict resolution.
7. Enterprise Engineering Practices
Editor Integration: VS Code
Install the Prettier - Code formatter extension, and enable format-on-save in your settings:
{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode"}This formats your code instantly upon pressing Ctrl + S (or Cmd + S).
.prettierrc: Recommended Configuration
{ "printWidth": 100, "tabWidth": 2, "semi": true, "singleQuote": true, "trailingComma": "all", "arrowParens": "always"}| Config Option | Recommended | Description |
|---|---|---|
printWidth |
100 | The default is 80; teams generally prefer it slightly wider to reduce unnecessary line breaks. |
tabWidth |
2 | The most common indentation width in the industry. |
semi |
true | Adds a semicolon at the end of statements to avoid issues with ASI (Automatic Semicolon Insertion). |
singleQuote |
true | Enforces single quotes. |
trailingComma |
all | This is actually the default since Prettier 3.0; explicitly writing it adds clarity. |
arrowParens |
always | Always wraps arrow function parameters in parentheses; default since Prettier 2.0. |
Explicitly writing defaults like trailingComma: "all" and arrowParens: "always" helps keep the configuration self-documenting.
Do not forget to add a .prettierignore file to prevent formatting build artifacts and third-party code:
node_modulesdistbuild.nextcoveragepnpm-lock.yamlpackage-lock.jsonAutomating Checks: Husky + lint-staged
“Format on save” cannot catch everyone—some might use editors without the extension, or submit code without saving. Setting up pre-commit hooks acts as the safety net:
npm install husky lint-staged -D{ "lint-staged": { "*.{js,ts,jsx,tsx}": ["prettier --write"] }}The workflow is:
graph TD A[git commit] --> B[lint-staged] B --> C[prettier --write] C --> D[Commit Complete]
CI/CD Solutions
Local hooks can still be bypassed with --no-verify. The final line of defense should reside on the server. A typical GitHub Actions workflow:
name: prettier-checkon: pull_request:jobs: prettier: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx prettier . --checkPRs that violate formatting guidelines will be blocked before merging, eliminating any manual slip-ups.
Monorepo Configurations
monorepo│├── apps│ ├── admin│ └── web│├── packages│ ├── ui│ └── utils│└── .prettierrcThe recommended approach is to place a single .prettierrc at the root of the repository. All sub-projects share the same rules, avoiding situations where admin uses single quotes while web uses double quotes. If a sub-directory has a specific requirement (e.g., Markdown files in a package requiring proseWrap: "always"), you can override rules locally using the overrides field in the root .prettierrc rather than maintaining separate config files.
8. Prettier Plugin Ecosystem
Tailwind CSS: Automating Class Sorting
Install the plugin:
npm install prettier-plugin-tailwindcss -DThen add it to the plugins array in your configuration (this step is easily omitted in tutorials):
export default { plugins: ['prettier-plugin-tailwindcss'],}The result:
<!-- Before --><button class="text-white px-4 sm:px-8 py-2 sm:py-3 bg-sky-700 hover:bg-sky-800">...</button><!-- After --><button class="bg-sky-700 px-4 py-2 text-white hover:bg-sky-800 sm:px-8 sm:py-3">...</button>The plugin automatically reorders classes according to Tailwind’s recommended sequence. It doesn’t alter visuals, but stops debates over class naming order. If you use other formatting plugins (e.g., import sorters), ensure prettier-plugin-tailwindcss is placed last in the plugins array, as it relies on the outputs of preceding plugins.
Astro Support
npm install prettier-plugin-astro -DIt is recommended to explicitly specify the parser for .astro files for better compatibility:
export default { plugins: ['prettier-plugin-astro'], overrides: [ { files: '*.astro', options: { parser: 'astro' }, }, ],}If you use the official Astro VS Code extension, it embeds Prettier and the plugin, so no extra editor configuration is needed. You only need to install it as a dependency for command line formatting or CI.
XML Support
npm install @prettier/plugin-xml -DAdd it to your plugins array to enable formatting for .xml files.
New Official Plugins: @prettier/plugin-oxc & @prettier/plugin-hermes
The Prettier team is actively evolving. In version 3.7 (released late 2025), they introduced two new standalone plugins: @prettier/plugin-oxc (providing faster JS/TS parsing via the Rust-based Oxc project) and @prettier/plugin-hermes (tailored for React Native’s Hermes engine syntax). These are not enabled by default but show Prettier’s path toward integrating faster parsers while maintaining output consistency.
9. Evolution of Prettier 3.x
Prettier 3.0 (released July 2023) brought multiple breaking changes beyond “embracing ESM”:
- ESM & Async Parsers: Plugins can be written in ESM, and config files support ESM. To support async parsing of embedded languages (like code blocks in Markdown), the
embedmethod signature in Printers was changed incompatibly. - Node.js Requirement Raised: Node 14 was the initial threshold for 3.0. The latest 3.8.x series now requires at least Node.js 20.
trailingCommaDefaults toall: With IE (the last browser not supporting trailing commas in function calls) retired in 2022, this legacy baggage was removed.
Key updates in post-3.0 versions:
| Version | Date | Key Changes |
|---|---|---|
| 3.1 | Late 2023 | Introduces experimental --experimental-ternaries for a more readable nested ternary formatting style. |
| 3.5 | Feb 2025 | Adds objectWrap and experimentalOperatorPosition options, and supports configuration files in TypeScript. |
| 3.6 | Mid 2025 | Releases experimental high-performance CLI (--experimental-cli), benchmarking several times faster on large codebases. |
| 3.7 | Nov 2025 | Standardizes Class and Interface formatting rules; releases @prettier/plugin-oxc and @prettier/plugin-hermes. |
| 3.8 | Late 2025 | Supports new syntax in Angular v21.1. |
Version 4.0 is currently in alpha, aiming to make the high-performance CLI the default behavior—spurred in part by performance competition from Rust-based formatters.
10. Limitations of Prettier
Prettier is not a silver bullet. It will not:
- Fix bugs, remove duplicate logic, or perform any refactoring.
- Optimize performance (e.g., rewriting inefficient
forloops). - Automatically organize import order (use
eslint-plugin-simple-import-sortorprettier-plugin-organize-importsfor this). - Check naming conventions or comment completeness—these still rely on humans or ESLint rules.
It does exactly one thing: ensures uniform code style. This focused scope is why it has run stably for a decade.
11. Rust-Based Formatters: A New Frontier
In recent years, high-performance tools written in Rust have begun challenging Prettier’s dominance.
Biome (formerly Meta’s Rome project) is a prime example, combining linting and formatting into a single binary. It boasts around 97% formatting compatibility with Prettier and offers a significant speed advantage on large codebases.
Oxfmt (created by the VoidZero team based on Oxc) takes a different route, aiming for maximum formatting compatibility with Prettier as a drop-in replacement. Benchmarks indicate it is orders of magnitude faster than Prettier and outpaces Biome, though it remains in early release stages.
This competition has pushed the Prettier team to optimize performance, leading to the experimental CLI and official Oxc parser plugin.
However, Prettier remains the de facto standard for now: it has the most mature ecosystem, widest language coverage, and largest selection of plugins. For stable projects, migration costs often outweigh performance gains, making the switch to a new toolchain a custom decision based on codebase size and maintenance costs.
12. Modern Frontend Pipeline Diagram
graph TD A[Developer Writes Code] --> B[VS Code Save Triggers Format] B --> C[Prettier Standardizes Code Style] C --> D[ESLint Inspects Code Quality] D --> E[git commit] E --> F[Husky Triggers Hook] F --> G[lint-staged Processes Changed Files] G --> H[GitHub Actions Double-Checks in CI] H --> I[Pull Request Merged]
Conclusion
Prettier changes team collaboration, not just aesthetic appeal. Before Prettier, developers argued over style; with Prettier, they discuss business logic.
When tools handle repetitive tasks, developers can focus on creating real value.
In summary: Prettier is not about making code prettier, but about saving teams from wasting time on code style.

