Back

Prettier Deep Dive: From AST to Enterprise Engineering Practices, Why Teams Choose It

11 min read

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 to
const name = "Tom";
if(condition){
// Change to
if (condition) {
const arr = [1,2,3]
// Change to
const 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.js

Each 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+b

Prettier’s first step is to parse it into an Abstract Syntax Tree (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:

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:

Input: const sum=(a,b)=>a+b Output:

const sum = (a, b) => a + b

The entire pipeline is:

4. Why Prettier’s Output is Always Stable

Because it doesn’t care what the original code looks like. Whether it is:

const a=1

or:

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.


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.

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:

eslint.config.js
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).

{
"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_modules
dist
build
.next
coverage
pnpm-lock.yaml
package-lock.json

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

Terminal window
npm install husky lint-staged -D
{
"lint-staged": {
"*.{js,ts,jsx,tsx}": ["prettier --write"]
}
}

The workflow is:

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-check
on:
pull_request:
jobs:
prettier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx prettier . --check

PRs that violate formatting guidelines will be blocked before merging, eliminating any manual slip-ups.

Monorepo Configurations

monorepo
├── apps
│ ├── admin
│ └── web
├── packages
│ ├── ui
│ └── utils
└── .prettierrc

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

Terminal window
npm install prettier-plugin-tailwindcss -D

Then add it to the plugins array in your configuration (this step is easily omitted in tutorials):

prettier.config.js
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

Terminal window
npm install prettier-plugin-astro -D

It is recommended to explicitly specify the parser for .astro files for better compatibility:

prettier.config.mjs
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

Terminal window
npm install @prettier/plugin-xml -D

Add 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 embed method 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.
  • trailingComma Defaults to all: 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 for loops).
  • Automatically organize import order (use eslint-plugin-simple-import-sort or prettier-plugin-organize-imports for 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

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.

Tags: PrettierEngineeringFrontendAST
Share to: