How much faster is the new Angular compiler?
The Angular team recently announced
that they were working on a new version of the compiler, called ngp, partly written in Rust.
They recently landed the bulk of this new compiler in a massive commit adding more than 370k lines, including around 56k lines of Rust. As I usually read all commits line by line, this one took me a long time 😅. But I dived in to get an idea of how much faster it is and, more importantly, how it works.
This blog post is an explanation of the compiler architecture, and a naive benchmark to check out what it's got!
Architecture of ngc
ngc is the version of the compiler we already know.
When Angular was first released in 2016,
this mechanism of pre-compiling templates to JS functions
with TypeScript type-checking was very innovative.
10 years later, this has been adopted in some way or another by all other frameworks. And, while type-safety in templates is awesome, the performance of this Angular compiler has always been a bit sub-par compared to the other ecosystems, especially on large projects, where it can take several minutes for a full cold build.
This partly comes down to how ngc is architected.
Its very first version was a wrapper around tsc, the TypeScript compiler.
Then, in version 4.3,
the Angular team leveraged the Transformer API introduced in TS v2.3.
Transformers rewrite the compiler’s representation of code before that code is emitted.
The compiler then evolved further with Ivy. Its initial Type Check Block (TCB) implementation landed in Angular 7's experimental Ivy compiler, before Ivy became the default in Angular 9. TCBs represent template expressions as TypeScript code for type-checking, separately from the runtime definitions and template instructions generated for the application.
To speed things up, a lot of our tools have taken the path of being rewritten from JS/TS in languages that compile to native binaries:
TypeScript v7 is a rewrite in Go, Rolldown is a rewrite of Rollup in Rust, etc.
The issue is that the Go version of tsc does not offer the same Transformer API (yet?),
and even if it does at some point, the cost of crossing the JS/Go boundary would be too high.
So the idea of the Angular team is two-fold:
- decouple Angular compilation from the TS Transformer API and separate type-checking from code emission
- write this new tool in Rust
Architecture of ngp
The new compiler is called ngp.
ngp separates Angular code generation from TypeScript type-checking.
Unlike the default ngc command,
it doesn’t itself run the TypeScript checker or emit JavaScript:
it produces TypeScript that downstream tools can check and build.
It is a hybrid tool: it's written in Rust, but still uses the existing template compiler written in TypeScript. So there are 3 steps:
- Rust analysis, using
oxc, VoidZero's low-level toolchain to parse/modify JS/TS code in Rust. This step discovers Angular declarations, reads the decorators and metadata, and resolves their relationships. - TypeScript code generation, reusing the existing
@angular/compilerfor template compilation. This step compiles templates and generates runtime code and type-check blocks (TCBs) in 2 different output files. - downstream processing: a TypeScript compiler checks the application and TCBs, while TypeScript or a bundler processes the runtime code into JavaScript.
As the analysis is done in Rust, it is much faster than its TS counterpart (it runs in parallel and streams compilation tasks), and consumes less memory.
TS code generation still uses the existing compiler, so no speed improvement here. We can imagine that this will change in the future, with a Rust version of this compiler as well (announced in the blog post from the team). The downstream processing is also a place where we'll gain time: it is no longer necessary to use TS v6, and TS v7 (the Go version) can be used to type-check. And TS v7 is several times faster than v6! In addition, a few alternative implementations of TS v7 have also appeared that could be even faster. More on that later.
The generation of JS code can be done in parallel by TS or by another tool, like esbuild (written in Go as well), whose specialty is to strip TypeScript syntax from the code to generate JS code, without type-checking (which is of course way faster).
The three phases in practice
Let's take a simple component with a signal and an inline template:
@Component({
selector: 'app-user',
template: '<p>Hello {{ name() }}</p>'
})
export class User {
readonly name = signal('Cédric');
}
1. Rust analysis
The Rust analyzer extracts the component's metadata and sends it to the TypeScript backend:
{
"className": "User",
"component": {
"selector": "app-user",
"template": "<p>Hello {{ name() }}</p>",
"standalone": true
}
}
The template is still a string: Rust has not compiled it or generated a TCB.
2. TypeScript code generation
The TypeScript backend uses the existing Angular template compiler to produce two files.
The first, user.ts, contains the runtime code, including this template function:
function User_Template(rf: number, ctx: any): any {
if (rf & 1) {
i0.ɵɵdomElementStart(0, 'p');
i0.ɵɵtext(1);
i0.ɵɵdomElementEnd();
}
if (rf & 2) {
i0.ɵɵadvance();
i0.ɵɵtextInterpolate1('Hello ', ctx.name());
}
}
The first branch creates the elements; the second updates the text using the signal's value.
The second file, user.ngtypecheck.ts, contains the TCB:
function _tcb1(this: i0.User) {
if (true) {
'' + this.name();
}
}
Notice that the runtime template function uses ctx: any,
whereas the TCB has a typed this: i0.User.
This lets TypeScript detect missing properties or invalid calls in the template.
3. Downstream processing
For this example, tsgo --noEmit checked both files without errors,
while esbuild independently emitted JavaScript, without bundling or minification.
The template function keeps its rendering instructions but loses its TypeScript annotations:
function User_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵdomElementStart(0, 'p');
i0.ɵɵtext(1);
i0.ɵɵdomElementEnd();
}
if (rf & 2) {
i0.ɵɵadvance();
i0.ɵɵtextInterpolate1('Hello ', ctx.name());
}
}
The TCB is only used for checking, not shipped as runtime code. And esbuild does not check types: that remains TypeScript's job.
At the time of writing this, ngp is still an internal tool
that the team is actively working on to bring up to par with the existing ngc pipeline.
The same test suite is run for both, and some tests are still failing for the new compiler.
We can also imagine that some optimizations will be done later,
but let's take a first look at the build times!
A naive benchmark
Note: This has been measured with a beta version of ngp (22.3.0-next.2) on a "CI-like" machine.
The same benchmark on macOS shows faster times, but similar gains.
I built the native version of the ngp compiler, then ran it on 3 projects:
- a small one with 15 components
- a medium one with 56 components
- and a large one with 1714 components
I used esbuild or Rolldown for per-file JavaScript emission, without bundling or minification. For type-checking, I used TypeScript v7.1 (beta at the time of writing, but faster than v7.0), and also tried tsrs, a port of the TS compiler in Rust by Max Schwenk and the Cloudflare/VoidZero team, which is even faster (but of course a bit experimental for now).
As you can see, we have nearly a 2-3x speedup already across all project sizes!
Memory usage is also way down: on the largest project it goes from 2.8Go to only 1.4Go.
ngp takes the bulk of the time, with the hatched area showing the estimated contribution of the existing template compiler.
Downstream tools are then quite fast,
with tsrs and Rolldown being faster than tsgo and esbuild.
Summary
This is already very promising, and there are certainly further gains to be made when the team focuses on performance optimization. We should also see a rewrite of the template compiler in the future, which will improve the times for the second phase. For the downstream part, we don't know yet which tools the Angular CLI team will use. The safe choice is TS itself of course, but it's interesting to see alternatives like tsrs that have popped up and offer further performance improvements!
All the times in the benchmark are cold builds. During development or tests, the incremental compilation should get the same order of magnitude of gains. It means that what sometimes takes 5-10 seconds to refresh on large projects should only take 2-3 seconds.
Can't wait to use this in our daily work!
← Older post
What's new in Angular 22.2?
Our books on sale


Next training sessions
- From Nov 16 to Nov 19, 2026Angular: Zero to Ninja(remote)
- From Dec 14 to Dec 17, 2026Angular: Ninja to Hero(remote)
