| |

Angular 123 🅰️ Angular Compiler and Ivy

⏱️ Estimated read: 13 min | |

The Angular compiler transforms templates, components, and decorators into JavaScript that the browser can execute. Since Angular 9, this work is done by Ivy, a complete rewrite of the previous View Engine that changed how compilation works at a fundamental level. Where View Engine compiled components and their NgModules together in a global analysis, Ivy compiles each component locally, using only the information available in the component’s own decorator and its direct imports. This “locality” is the architectural property that makes incremental builds faster, bundle sizes smaller, and error messages clearer.

The compiler itself is a pipeline with distinct phases. It parses TypeScript and extracts Angular metadata from decorators. It builds a graph of NgModules and their scopes. It compiles each component’s template into rendering instructions. It generates TypeScript type-check blocks that let the TypeScript compiler validate template expressions. And it emits JavaScript that contains the compiled component definitions. Each phase has a specific responsibility, and understanding them explains why the compiler behaves the way it does.

This chapter covers the compiler’s role in the Angular build, the difference between Ivy and View Engine, the compilation pipeline from metadata extraction to code generation, the difference between AOT and JIT compilation, template type checking, and the compiler options that control the output.

Key point: The Angular compiler transforms decorators and templates into JavaScript. Ivy, the current engine, compiles components locally using only local information, which enables faster incremental builds and smaller bundles. AOT compilation runs at build time and produces optimized code without the compiler in the bundle; JIT compilation runs in the browser and is used only in specific development scenarios. The compiler generates type-check blocks that TypeScript validates to catch template errors at build time.


Why the compiler exists

The declarative-template problem. Angular templates are not HTML. They contain directives, bindings, pipes, and structural syntax that the browser does not understand. The compiler transforms these templates into JavaScript instructions that create and update the DOM. Without the compiler, the browser would have no way to interpret *ngIf or {{ expression }}.

The performance problem. A template that is compiled at runtime requires a compiler in the browser, which adds to the bundle size and delays the first render. Compiling at build time removes the compiler from the bundle and produces code that the browser can execute immediately. This is the core advantage of AOT compilation.

The type-safety problem. TypeScript type-checks the component class, but it knows nothing about the template. The Angular compiler generates a type-check block (TCB) that expresses the template’s expressions as TypeScript code, which TypeScript then checks. This catches errors like {{ user.nmae }} before the application runs .

The locality problem. View Engine required a global analysis of the entire application to compile a single component, because a component’s template scope depended on its NgModule and that module’s imports. Ivy removes this dependency by embedding the scope information in the component itself. The compiler can now compile a component without looking at any other part of the application .

The ecosystem problem. Libraries are distributed in a compiled format. The compiler’s output format must be stable across versions so that a library built with one version of Angular can be consumed by an application built with another. The compilationMode option controls whether the output is fully compiled (full) or in an intermediate, version-stable format (partial) .


a. Ivy versus View Engine

Ivy and View Engine are the two compilation and rendering engines that Angular has used. View Engine was the default from Angular 4 through Angular 8. Ivy became the default in Angular 9 and replaced View Engine entirely by Angular 12 .

AspectView EngineIvy
Compilation modelGlobal analysisLocal compilation
Component scopeDefined by NgModuleDefined by the component
Build overheadHigherLower (~40% reduction)
Tree-shakingLimitedBetter
Bundle sizeLargerSmaller
Error messagesVerboseClearer
Incremental buildsSlowerFaster
entryComponentsRequiredNot required

The locality of Ivy is the key difference. In View Engine, a component’s template scope — the set of directives and pipes available in the template — was determined by the NgModule that declared it. The compiler had to analyze the entire NgModule graph to know what a component could use. In Ivy, the scope is expressed in the component’s own compiled definition, so the compiler can work on one component at a time .

This is what enables incremental compilation. When a template changes, only that component needs to be recompiled. When a component’s class changes, only the components that depend on it need to be recompiled. The language service exploits this to provide fast editor feedback as you type .


b. The compilation pipeline

The Angular compiler is a multi-phase pipeline that transforms TypeScript with Angular decorators into JavaScript with compiled component definitions.

Phase 1: Metadata extraction. The compiler reads the TypeScript source and extracts Angular metadata from decorators like @Component, @Directive, @NgModule, and @Pipe. The metadata collector evaluates expressions in the decorator to the extent possible, folding constants and inlining template strings. Expressions that cannot be folded are recorded as an AST for the compiler to resolve later .

The collector can fold literal objects, arrays, strings, numbers, and simple binary and conditional expressions. It cannot fold function calls, new expressions, or spreads in arrays. When an expression cannot be folded, the compiler may rewrite it into an exported variable during a later phase .

Phase 2: Scope analysis. The compiler builds a graph of NgModules, their declarations, imports, and exports. It uses this graph to determine the compilation scope of each component — the set of directives, components, and pipes available in its template. In View Engine this was a global analysis; in Ivy the scope is embedded in the component’s compiled definition .

Phase 3: Template type checking. The compiler generates a type-check block for each component’s template. The TCB is TypeScript code that mirrors the template’s expressions and bindings, allowing TypeScript to validate them. Errors in the TCB are mapped back to the original template location and reported to the developer .

Phase 4: Template compilation. The compiler transforms the template into rendering instructions. These instructions create elements, set properties, attach event listeners, and manage embedded views. The output is an Ivy component definition (a ɵcmp factory) that the runtime executes .

Phase 5: Code generation. The compiler emits JavaScript that contains the compiled component definitions. The output format depends on the compilation mode: full produces fully AOT-compiled code; partial produces an intermediate format suitable for published libraries .


c. AOT versus JIT

AOT and JIT are not different compilers. Angular has one compiler. The difference is when it runs.

AspectAOTJIT
When it runsBuild timeRuntime (browser)
Compiler in bundleNoYes
Bundle sizeSmallerLarger
Initial renderFasterSlower
Error detectionBuild timeRuntime
Default sinceAngular 9Before Angular 9

With AOT, the compiler runs during the build and produces JavaScript that contains the compiled templates. The browser downloads and runs the code directly. The Angular compiler is not included in the bundle, which reduces size and eliminates the runtime compilation step .

With JIT, the compiler runs in the browser when the application bootstraps. The compiler is part of the bundle, and templates are compiled at runtime. This was the default before Angular 9 and is now used in specific scenarios where templates are generated dynamically or when the build system cannot run AOT .

Since Angular 9, AOT is the default even in development mode. This means ng serve benefits from the same compile-time checking as production builds, and the developer sees template errors immediately rather than at runtime .


d. Template type checking

Template type checking is one of the compiler’s most valuable features. It catches errors in template expressions that would otherwise surface only when the application runs.

The compiler generates a type-check block (TCB) for each component. The TCB is a TypeScript function that mirrors the template’s bindings and expressions. For a template like <p>Name: {{user.name}}</p>, the TCB contains a statement that accesses ctx.user.name, which TypeScript then checks against the component’s types .

Type narrowing works in templates the same way it works in TypeScript. An *ngIf="person" narrows the type of person within the block, so {{person.address.street}} does not produce an “Object is possibly ‘undefined'” error when the *ngIf guarantees that person is defined .

The strictTemplates compiler option enables the most thorough checking. Without it, the compiler performs basic checks; with it, the checking matches the strictness of the TypeScript compiler’s strict mode .

The non-null assertion operator (!) can be used in templates to suppress “possibly undefined” errors when the developer knows a value is non-null. The Angular documentation recommends using it sparingly, because a refactor can break the constraint that makes it safe .


e. Compiler options

The angularCompilerOptions block in tsconfig.json controls the compiler’s behavior.

OptionPurposeDefault
strictTemplatesEnable full template type checkingfalse
compilationModefull for applications, partial for librariesfull
annotationsAsEmit decorators as static fields or leave themstatic fields
enableI18nLegacyMessageIdFormatUse legacy i18n message IDstrue
enableResourceInliningInline templates and stylestrue for CLI libraries
disableExpressionLoweringDisable metadata rewritingtrue

The annotationsAs option is relevant for tree-shaking. The default static fields replaces decorators with static fields, which allows advanced tree-shakers like Closure Compiler to remove unused classes. The decorators value leaves decorators in place, which makes compilation faster but prevents effective tree-shaking .

The compilationMode option is the most important for libraries. The partial mode produces a stable intermediate format that is compatible across Angular versions. Libraries published to npm should use partial. Applications and libraries built in the same repository with the same Angular version can use full .


Complete Example Session

// ============================================
// PART 1: A COMPONENT WITH A TEMPLATE
// ============================================
import { Component } from '@angular/core';

@Component({
  selector: 'app-hero',
  template: `
    <h1>{{ title }}</h1>
    <p>{{ hero.name }}</p>
    <button (click)="increment()">+1</button>
  `,
})
export class HeroComponent {
  title = 'Hero';
  hero = { name: 'Alice', age: 30 };

  increment() {
    this.hero.age++;
  }
}
// ============================================
// PART 2: TYPE CHECK BLOCK (GENERATED)
// ============================================
// The compiler generates a TCB similar to this:
function HeroComponent_TCB(ctx: HeroComponent) {
  '' + ctx.title;
  '' + ctx.hero.name;
  ctx.increment();
}
// TypeScript checks this code and reports errors
// mapped back to the template.
// ============================================
// PART 3: ENABLE STRICT TEMPLATES
// ============================================
{
  "angularCompilerOptions": {
    "strictTemplates": true
  }
}
// ============================================
// PART 4: TEMPLATE ERROR CAUGHT BY TCB
// ============================================
// Template: {{ hero.nmae }}
// Error: Property 'nmae' does not exist on type '{ name: string; age: number; }'
// ============================================
// PART 5: LIBRARY COMPILATION MODE
// ============================================
{
  "angularCompilerOptions": {
    "compilationMode": "partial"
  }
}
// ============================================
// PART 6: APPLICATION COMPILATION MODE
// ============================================
{
  "angularCompilerOptions": {
    "compilationMode": "full"
  }
}
# ============================================
# PART 7: AOT BUILD
# ============================================
ng build --configuration production
# The compiler runs at build time
# Output contains compiled component definitions
# No compiler in the bundle
# ============================================
# PART 8: JIT BUILD (LEGACY)
# ============================================
ng serve
# Since Angular 9, AOT is used even in dev mode
# JIT is only used for specific scenarios
// ============================================
// PART 9: ANNOTATIONS AS STATIC FIELDS
// ============================================
{
  "angularCompilerOptions": {
    "annotationsAs": "static fields"
  }
}
// Enables better tree-shaking
# ============================================
# PART 10: VERIFY COMPILED OUTPUT
# ============================================
# The compiled component definition contains:
# - ɵcmp (component definition)
# - ɵfac (factory function)
# - Template rendering instructions
# These are what the runtime executes

These ten parts cover a component with a template, the generated type-check block, enabling strict templates, a template error caught by the TCB, library compilation mode, application compilation mode, AOT build, JIT build, annotations as static fields, and the compiled output.


Quick Reference

Ivy vs View Engine

AspectView EngineIvy
CompilationGlobalLocal
Build overheadHigherLower
Tree-shakingLimitedBetter
Bundle sizeLargerSmaller
entryComponentsRequiredNot required
IncrementalSlowerFaster

Compilation Phases

PhasePurpose
Metadata extractionExtract decorator metadata
Scope analysisBuild NgModule graph
Template type checkingGenerate TCB for TypeScript
Template compilationGenerate rendering instructions
Code generationEmit JavaScript

AOT vs JIT

AspectAOTJIT
WhenBuild timeRuntime
Compiler in bundleNoYes
Bundle sizeSmallerLarger
DefaultAngular 9+Before Angular 9

Compiler Options

OptionPurposeDefault
strictTemplatesFull template checkingfalse
compilationModefull or partialfull
annotationsAsDecorator emissionstatic fields
enableResourceInliningInline templatestrue (CLI libs)

Type-Check Block

AspectDetail
PurposeLet TypeScript check templates
OutputSynthetic TypeScript function
ErrorsMapped back to template
Type narrowingSupported via *ngIf

Best Practices

✅ Do This:

// Enable strict template checking
{ "angularCompilerOptions": { "strictTemplates": true } }
// Use partial mode for published libraries
{ "angularCompilerOptions": { "compilationMode": "partial" } }
// Make template-bound members public
export class HeroComponent {
  title = 'Hero';  // public, accessible in template
}

❌ Don’t Do This:

// Private members in templates
export class HeroComponent {
  private title = 'Hero';  // ❌ AOT error
}
// Using full mode for a published library
{ "angularCompilerOptions": { "compilationMode": "full" } }  // ❌ version skew

Common Pitfalls

PitfallWhy It HappensFix
Template error at runtimeJIT or no strictTemplatesEnable AOT and strictTemplates
Private member errorPrivate member bound in templateMake it public or protected
Library version skewFull mode in published libraryUse partial mode
Larger bundleJIT compilationUse AOT
Slow incremental buildsGlobal compilation (View Engine)Upgrade to Ivy

Real-World Examples

1. Strict Templates

{ "angularCompilerOptions": { "strictTemplates": true } }

2. Library Partial Mode

{ "angularCompilerOptions": { "compilationMode": "partial" } }

3. Type Narrowing with *ngIf

<div *ngIf="person">
  {{ person.address.street }}
</div>

4. Non-Null Assertion

{{ person.address!.street }}

5. AOT Build

ng build --configuration production

6. Check Compiled Output

grep "ɵcmp" dist/*/main.js

7. Annotations for Tree-Shaking

{ "angularCompilerOptions": { "annotationsAs": "static fields" } }

8. Resource Inlining

{ "angularCompilerOptions": { "enableResourceInlining": true } }

9. View Engine Migration

ng update @angular/core @angular/cli

10. Language Service

{ "angularCompilerOptions": { "strictTemplates": true } }

Visual

Compiler Pipeline

┌──────────────────────────────────────────────────────────────┐
│  TypeScript source with Angular decorators                   │
│       │                                                      │
│       ▼                                                      │
│  METADATA EXTRACTION                                         │
│  └── Extract @Component, @Directive, @NgModule               │
│       │                                                      │
│       ▼                                                      │
│  SCOPE ANALYSIS                                              │
│  └── Build NgModule graph, determine template scope          │
│       │                                                      │
│       ▼                                                      │
│  TEMPLATE TYPE CHECKING                                      │
│  └── Generate TCB → TypeScript checks → errors reported      │
│       │                                                      │
│       ▼                                                      │
│  TEMPLATE COMPILATION                                        │
│  └── Generate Ivy rendering instructions                     │
│       │                                                      │
│       ▼                                                      │
│  CODE GENERATION                                             │
│  └── Emit JavaScript with compiled component definitions     │
└──────────────────────────────────────────────────────────────┘

Ivy Locality

┌──────────────────────────────────────────────────────────────┐
│  VIEW ENGINE:                                                │
│  Component ──▶ NgModule ──▶ Imports ──▶ Global analysis      │
│  └── Cannot compile component without whole app              │
│                                                              │
│  IVY:                                                        │
│  Component ──▶ Local scope ──▶ Compiled definition           │
│  └── Compile component independently                         │
│  └── Faster incremental builds, better tree-shaking          │
└──────────────────────────────────────────────────────────────┘

AOT vs JIT

┌──────────────────────────────────────────────────────────────┐
│  AOT (Angular 9+ default):                                   │
│  Build time: compiler runs → produces compiled JS            │
│  Browser: downloads compiled JS → renders immediately        │
│  Bundle: no compiler                                         │
│                                                              │
│  JIT (legacy):                                               │
│  Build time: TypeScript compiled to JS                       │
│  Browser: downloads compiler + templates → compiles → renders│
│  Bundle: includes compiler                                   │
└──────────────────────────────────────────────────────────────┘

Type-Check Block

┌──────────────────────────────────────────────────────────────┐
│  Template: <p>{{user.name}}</p>                              │
│       │                                                      │
│       ▼                                                      │
│  TCB: function C_TCB(ctx: C) {                               │
│         '' + ctx.user.name;                                  │
│       }                                                      │
│       │                                                      │
│       ▼                                                      │
│  TypeScript checks: ctx.user.name is valid                   │
│  If invalid: error mapped to template location               │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
CompilerTransforms decorators and templates to JS
IvyLocal compilation, Angular 9+
View EngineGlobal compilation, deprecated
AOTBuild-time compilation, default since Angular 9
JITRuntime compilation, legacy
LocalityIvy compiles components independently
TCBType-check block for template validation
strictTemplatesFull template type checking
compilationModefull for apps, partial for libraries
annotationsAsstatic fields for tree-shaking
Incremental buildsFaster with Ivy locality

Key takeaways:

  • The Angular compiler transforms declarative templates into executable JavaScript. Without it, the browser would have no way to interpret directives, bindings, and structural syntax.
  • Ivy is the current engine and compiles locally. Each component’s template scope is expressed in its own compiled definition, so the compiler can work on one component at a time. This enables faster incremental builds and better tree-shaking .
  • AOT is the default since Angular 9. The compiler runs at build time, the compiler is not included in the bundle, and template errors are caught before the application runs .
  • Template type checking uses type-check blocks. The compiler generates TypeScript code that mirrors the template’s expressions, and TypeScript validates it. Errors are mapped back to the template location .
  • The strictTemplates option enables full checking. Without it, only basic checks are performed. With it, the checking matches the strictness of TypeScript’s strict mode .
  • The compilationMode option distinguishes applications from libraries. Applications use full for optimized output; published libraries use partial for version compatibility .
  • The annotationsAs option controls tree-shaking. The default static fields replaces decorators with static fields, which allows advanced tree-shakers to remove unused code .
  • Ivy removed the need for entryComponents. Components are discovered and compiled automatically from their usage, which simplifies the configuration .

Remember: The Angular compiler is the bridge between the declarative code you write and the JavaScript the browser executes. Ivy, the current engine, is a complete rewrite of View Engine that compiles components locally, which makes builds faster and bundles smaller. The compiler has distinct phases: metadata extraction, scope analysis, template type checking, template compilation, and code generation. AOT compilation runs at build time and is the default since Angular 9; JIT compilation runs in the browser and is legacy. Template type checking catches errors before runtime by generating type-check blocks that TypeScript validates. The compiler options in tsconfig.json control the strictness of checking, the output format for libraries, and the tree-shaking behavior. Understanding the compiler is understanding how Angular turns templates into running applications.


Was this helpful?


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!