
Intermediate workshop
Nx for Scalable Architecture
Master Nx to enforce architecture, speed up your development workflow and improve code quality
Learn more

In a large enterprise Nx monorepo with more than 68,000 files, formatting had become a recurring CI tax. nx format took 4m 34s to check the whole workspace and 5m 09s to write it, making the cost of formatting increasingly noticeable at this scale.
We set out to reduce that overhead by migrating from Prettier to oxfmt, the Rust-based formatter from the Oxc project. The result: the full-workspace formatting check dropped from 4m 34s to 17.8s, a 15.4× speedup, while the write dropped from 5m 09s to 49.8s.
But getting there wasn't a simple formatter swap. The Angular application relied on formatting behavior that oxfmt couldn't fully replace, so we built a Prettier-to-oxfmt hybrid approach: oxfmt handles ~99% of the files, while Prettier remains responsible for the Angular templates that still require it.
In this article, we break down how we migrated a large enterprise Nx workspace from Prettier to a hybrid oxfmt setup, the compatibility issues we encountered along the way, and the performance gains we measured in practice. We’ll also cover the trade-offs of the approach and provide the setup in a reproducible demo.

oxfmt picks its HTML parser from the filename, so it parses an Angular template not named *.component.html as plain HTML. Prettier lets you override the parser per file glob; oxfmt does not.
overrides: [
{
files: "*.html",
options: {
parser: "angular",
},
},
];
We kept both formatters behind one yarn format script that routes:

oxfmt: TypeScript, JavaScript, JSON, SCSS, and *.component.html.
Prettier: every other .html, where the Angular parser has to be declared by path.
Once the file routing was in place, three issues prevented the migration from being a straightforward Prettier-to-oxfmt replacement: ignore-file conflicts, differences in import sorting, and the loss of nx format
Ignore Conflict: oxfmt reads .prettierignore
Import Sorting Parity: oxfmt can't load Prettier plugins, so its native sorting replaces @ianvs/prettier-plugin-sort-importseslint-disablepartitionByComment: true// in
import { zeta, alpha, Mid } from "pkg-b";
import { OnInit, Component, computed } from "@angular/core";
// oxfmt out: statements reordered, specifiers untouched
import { OnInit, Component, computed } from "@angular/core";
import { zeta, alpha, Mid } from "pkg-b";
// Prettier + @ianvs out: both levels sorted
import { Component, computed, OnInit } from "@angular/core";
import { alpha, Mid, zeta } from "pkg-b";sort-imports
Replacing workspace-level wrapper: nx format
With the hybrid setup in place, the next question was how much of oxfmt's raw performance advantage would survive in the actual CI workflow.
Raw tool runs and end-to-end workflow are two different numbers, and mixing them oversells the result. Check and write are two more: CI waits on the check, a reformat pays for the write. The tables below keep all four apart.
The theoretical ceiling after swapping the core engine: Prettier and oxfmt called directly, no Nx, no routing script.
Time to check the tree, wall-clock. Both formatters called directly - no Nx, no routing script. Lower is better.

Source: prettier-vs-oxfmt final benchmark, 2026-09-10 Library = median of 3 runs; workspace = single run. Write mode: 29.4 s vs 1.8 s (16x) and 4 m 55 s vs 11.3 s (26x).
Scope | Action | Prettier (median) | oxfmt (median) | Speedup |
Single Library (5,446 files) | check | 35.4s | 1.8s | 20× |
Single Library (5,446 files) | write | 29.4s | 1.8s | 16× |
Whole Workspace (67,899 files) | check | 4m 35s | 10.4s | 26× |
Whole Workspace (67,899 files) | write | 4m 55s | 11.3s | 26× |
Library rows are the median of 3 runs; workspace rows are a single run each.
Whole-workspace check. Top two bars at true scale; below, the hybrid run magnified to show what its 17.8 s is made of.

Source: combined format benchmark, 2026-09-10, median of 3 runs. Total is one end-to-end hybrid run; tool times are that tool's median inside those runs. Nx measured cold-graph.
oxfmt takes ~99% of the files; Prettier keeps 599 plain HTML files. Those 599 files cost 6.74 s of a 17.8 s whole-workspace check: 38% of the time for under 1% of the files. Per file, Prettier runs about 70× slower than oxfmt here.
Formatter | Files processed | Files changed | Share | Check (s) | Write (s) | Handles |
oxfmt | 67,899 | 50,875 | 99.1% | 10.99 | 25.49 | TS/JS/JSON/SCSS + |
Prettier | 599 | 94 | 0.9% | 6.74 | 12.04 | plain non-component |
Total (one end-to-end run) | 68,498 | 50,969 | 100% | 17.79 | 49.80 | — |
All rows come from the same runs: the total is the median end-to-end wall-clock of one hybrid run, and the per-tool columns are that tool's median inside those runs, so they need not add up to the total. We flipped singleQuote so every file needed formatting and both setups did real work.
The engine ceiling is 26×; Nx orchestration and the Prettier tail bring the end-to-end check to 15×, and a full rewrite to 6×.
End-to-end wall-clock, nx format (Prettier) vs the hybrid script, same session. Solid = check; tinted = what write adds on top. Full bar = write.

Source: combined format benchmark, 2026-09-10. Median of 3 runs per value. Nx measured cold-graph (nx reset before every run); every file forced dirty so both setups do real work. The tinted segment is write minus check, so the whole bar is measured write time.
Scope | Action | Nx Format (median) | Hybrid Script (median) | Real-world Speedup |
Single Library | check | 68.8s | 13.9s | 5.0× |
Single Library | write | 88.4s | 16.4s | 5.4× |
Whole Workspace | check | 4m 34s | 17.8s | 15.4× |
Whole Workspace | write | 5m 09s | 49.8s | 6.2× |
Median of 3 runs per cell, all from one benchmark session. Nx numbers are cold-graph: nx reset
Since Nx 23.2, nx format
Nx 23.2 provides replacement for file-selection half of our script (--all--base--head--files
With Nx 23.2 you already get oxfmt support and the speed with it. One thing you don't get is full file coverage. In our case keeping *.htmlnx format
Everything above runs in a public Nx + Angular workspace:
github.com/push-based/oxfmt-nx-demo
Two branches, so the migration reads as a diff rather than a finished state:
Branch | State |
|
| Before: a mature Prettier setup, plugin and all | Prettier over the whole workspace |
| After: the hybrid script | oxfmt for ~99% of files, Prettier for the rest |
git diff main oxfmt-hybridoxfmt-hybrid.oxfmtrc.json
What started as a slow nx format
In this case, a full replacement wasn't yet practical. Keeping Prettier for the small subset of Angular templates that still required it, while moving the remaining ~99% of files to oxfmt, gave us a pragmatic middle ground. The result was a full-workspace CI formatting check reduced from 4m 34s to 17.8s, while preserving the formatting behavior the application still required.
As oxfmt's Angular parser support evolves, the remaining Prettier dependency can eventually disappear. Until then, the hybrid approach provides the performance benefits available today while preserving the formatting behavior that oxfmt cannot yet cover.
If you're working with a large Nx or Angular environment, you may be seeing similar friction elsewhere: CI steps that have grown from seconds to minutes, tooling that no longer scales with the codebase, or development workflows that have become harder to change without affecting something else. At that scale, those delays can compound across teams, builds, and release cycles.
Explore your setup with us to take a closer look at where that time is going, what is driving it, and which changes could make a meaningful difference without compromising the systems already in place.

Intermediate workshop
Master Nx to enforce architecture, speed up your development workflow and improve code quality
Learn more
Fully exploit the potential of Nx with Nx Enterprise consulting! We provide guidance through workshops and trainings too. Get in Contact with our experts!
Learn more