Ivy is now turned on by default

Earlier Angular releases required an explicit opt-in to use Ivy. With version 9, the situation is reversed: you must explicitly disable Ivy to return to View Engine. This fallback option remains available in both versions 9 and 10 to ease the migration path.

While libraries can be AOT-compiled directly into Ivy instructions and metadata, the Angular team advises against it. Their recommended strategy for version 9 is to publish libraries that are AOT-compiled and View Engine-compatible. When such a library is used inside an Ivy-based application, the Angular compatibility compiler upgrades it to Ivy at install time.

See “The Angular Ivy guide for library authors” for details on library compatibility and the transition plan.

{
  "//": "tsconfig.json",
  "angularCompilerOptions": {
    "enableIvy": false
  }
}
Enter fullscreen mode Exit fullscreen mode
Listing 1A. TypeScript configuration: Opting out of Ivy to fall back to View Engine.
// polyfills.ts
// Only used in multilingual Ivy applications
// import '@angular/localize/init';
Enter fullscreen mode Exit fullscreen mode
Listing 1B. Polyfills: Opting out of Ivy to fall back to View Engine.

Should Ivy cause issues in your application or in any dependency you rely on, you can disable it and revert to View Engine. This is done by unsetting the enableIvy Angular compiler option and turning off @angular/localize, as shown in Listings 1A and 1B.

Opting out of Ivy in a server context requires extra care. Consult the official guide for disabling Ivy with server-side rendering.

The principle of locality

Under View Engine, compiling a component required knowledge of all its declarable dependencies, as well as the dependencies of those dependencies. This chain of metadata meant that libraries could not be AOT-compiled with View Engine.

Ivy changes that requirement. To compile a component, Angular now needs details only about that component itself — apart from the names and package names of its declarable dependencies. In particular, metadata from those dependencies is no longer necessary.

Because of this principle of locality, build times should generally improve.

Lazy-loaded components

The entryComponents declaration is deprecated since it is no longer required. Any Ivy component can now be lazy loaded and rendered dynamically.

This opens the door to lazy loading and rendering a component without relying on routing or Angular modules. In practice, though, you will still need component render modules or feature render modules to connect a component’s template with its declarable dependencies.

Libraries used exclusively by a lazy loaded component are even included in the lazy-loaded chunks.

Differential loading refinements

When differential loading arrived in Angular version 8, the entire build ran twice: once for the ES5 bundle and once for the ES2015+ bundle.

In version 9, the build produces an ES2015+ bundle first, then transpiles that output into a separate ES5 bundle. This avoids running a full build process twice.

AOT compilation is everywhere

AOT is now the default for builds, the development server, and even tests. In earlier versions, JIT was preferred for development and testing because AOT was considerably slower. With Ivy’s faster build and rebuild cycles, AOT offers a much smoother workflow.

Previously, mixing JIT for some stages and AOT for the final build meant that errors could surface only during production builds — or worse, at runtime.

Bundle sizes

Ivy helps reduce bundle sizes through the Ivy Instruction Set, a collection of tree-shakable runtime rendering instructions. The resulting bundles contain only the rendering instructions actually used in your project.

This is particularly useful for microfrontends, Angular Elements, and web apps where Angular does not manage the entire document.

That said, the size difference between View Engine and Ivy bundles depends on your application’s size and the third-party libraries in use. In general:

  • Small, straightforward applications tend to see a significant reduction in bundle size.
  • Complex applications may see a larger main bundle, but smaller lazy-loaded bundles.

For large applications, the combined bundle size drops considerably. For medium-sized ones, the overall size might actually increase. In both scenarios, the main bundle is likely to grow, which can hurt initial page load performance.

Globalisation

Locales — including number formatting, date formatting, and other regional settings — can now be loaded dynamically at runtime rather than being registered at compile time.

// main.ts
import '@angular/localize/init';

import { loadTranslations } from '@angular/localize';

loadTranslations({
  '8374172394781134519': 'Hello, {$username}! Welcome to {$appName}.',
});
Enter fullscreen mode Exit fullscreen mode
Listing 2. Dynamically loading translations.

As shown in Listing 2, translated texts can also be loaded at runtime instead of being baked into your bundles.

These translated strings could be fetched from a database or a file.

One bundle, many languages

Switching languages requires restarting the application, but you no longer need to serve a different bundle for each language.

With some configuration, this allows a single application bundle on one hostname to support multiple languages.

Compile-time inlining

Localised applications are now compiled exactly once. Rather than running multiple builds to generate one bundle per language, each language bundle is produced by replacing $localize placeholders with the appropriate translated text.

The @angular/localize package is now required for localisation support. On the upside, if your application uses a single language, Angular’s localisation code is no longer included in the bundle.

For applications without localised templates, the i18n* Ivy instructions are tree-shaken out of the final bundle.

Localisable texts in component models and services

// app.component.ts
@Component({
  template: '{{ title }}',
})
export class AppComponent {
  title = $localize`Welcome to MyApp`;
}
Enter fullscreen mode Exit fullscreen mode
Listing 3. A translation text placeholder in a component model.

A new capability in internationalisation is the ability to include translation placeholders in component models, as shown in Listing 3. Previously, this was limited to templates.

Additional provider scopes

Angular modules have always provided a scope for providers. Angular version 6 introduced the 'root' provider scope along with tree-shakable providers for both root and module scopes.

Version 9 adds two more scopes: 'platform' and 'any'. Platform-scoped providers are shared across multiple Angular applications mounted in the same document. The 'any' scope gives one provider instance per module injector — for example, a single instance for the eagerly loaded main bundle and another for each lazy loaded Angular module.

Improved developer experience

With Ivy, the Angular Language Service can perform additional checks during development. This is a notable boost to developer productivity.

File path checks

The Angular Language Service now continuously validates the file paths for component stylesheets and templates.

Template type checks

Templates are type-checked according to the mode described in the “Strict mode” section. Member names and types are verified, including inside embedded views. Issues that used to surface as runtime errors are now caught during development and build time.

New debugging API in development mode

ng.probe has been superseded by a new debugging API available in development mode. The two most prominent functions are ng.applyChanges and ng.getComponent.

Strict mode

Strict workspace schematic

The ng new workspace schematic now accepts the --strict flag, which is turned off by default (false).

ng new my-app --strict
Enter fullscreen mode Exit fullscreen mode

Enabling this flag introduces a series of stricter TypeScript compiler settings, as demonstrated in Listing 4.

{
  "//": "tsconfig.json",
  "compilerOptions": {
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noFallthroughCasesInSwitch": true,
    "strictNullChecks": true
  }
}
Enter fullscreen mode Exit fullscreen mode
Listing 4. TypeScript compiler options enabled in a strict Angular workspace.

Notably, these settings don’t align exactly with what you’d get from setting "strict": true in the compilerOptions object. Let’s examine how the Angular workspace strict option differs from the TypeScript compiler strict option.

The shared options between the two include:

  • noImplicitAny
  • noImplicitThis
  • strictNullChecks

Beyond those, the Angular workspace strict option also turns on:

  • noImplicitReturns
  • noFallthroughCasesInSwitch

while the TypeScript compiler strict option adds:

  • alwaysStrict
  • strictBindCallApply
  • strictFunctionTypes
  • strictPropertyInitialization

Additionally, the Angular workspace strict option does not enable the new strict template type checking mode; it only activates the previous full mode.

Strict template type checking

Since Angular version 5, developers have been able to opt into template type checking by setting "fullTemplateTypeCheck": true in the angularCompilerOptions object.

With Ivy, a stricter form of template type checking is introduced, as shown in Listing 5. When this new Angular compiler option is enabled, the fullTemplateTypeCheck setting is disregarded.

{
  "//": "tsconfig.json",
  "angularCompilerOptions": {
    "strictTemplates": true
  }
}
Enter fullscreen mode Exit fullscreen mode
Listing 5. Enable strict template type checking.

Strict template type checking validates the types of property bindings and adheres to the strictNullChecks flag. It also verifies the types of template references to directives and components, including those with generic types. Additionally, the types of template context variables are checked, which is beneficial for NgFor loops. The $event type in event bindings and animations is also validated, along with the types of native DOM elements.

These stricter validations can result in errors or false positives in certain situations, such as when using libraries that weren’t compiled with strictNullChecks. To handle this, strict template type checking includes options to opt out or customize the checks. For instance, strictTemplates actually serves as a shortcut for 8 distinct Angular compiler options.

Enhanced component and directive class inheritance

Base classes without selectors are now supported for directives and components. Certain metadata is now inherited from base component and directive classes. This simplifies the process of extending Angular Components and Angular Router directives.

Latest TypeScript versions

Angular version 9 supports TypeScript versions 3.6 and 3.7, while older TypeScript versions are no longer supported. See Table 1 for a comparison of TypeScript compatibility across all Angular versions.

Table 1. Angular CLI, Angular, Node.js and TypeScript compatibility table. Open in new tab.

TypeScript version 3.6 brings these features, among others:

  • Unicode support for identifiers in modern targets
  • Better developer experience for promises
  • More rigorous type checking for generators

TypeScript version 3.7 brings these features that are usable with Angular version 9:

  • Optional chaining operator (?.) analogous to Angular templates' safe navigation operator
  • Nullish coalescing operator (??)
  • Assertion functions (assert parameterName is typeName and asserts parameterName)
  • Top-level await
  • Enhanced recursive type aliases
  • Improved developer experience for functions, including truthy checks

Improved server-side rendering with Angular Universal

Angular Universal version 9 is released alongside a Node.js Express development server, providing a realistic environment during development.

This release also includes an Angular CLI builder for prerendering static routes using guess-parser, inspired by angular-prerender. A routes file can be supplied to prerender dynamic routes that contain parameters.

How do I get started?

Angular Universal can be added via the command ng add @nguniversal/express-engine. To launch the server-side rendering development server with live reload, use the builder command ng run myapp:serve-ssr. Similarly, ng run myapp:prerender detects static and dynamic routes and prerenders them.

Refined styling experience

Styling in Angular Ivy has undergone a significant rework. Combining static HTML classes with the NgStyle and NgClass directives is now fully supported and easier to understand.

CSS Custom Properties support

As part of the Ivy styling overhaul, binding CSS Custom Properties is now possible.

An example binding is shown below:

<div [style.--my-var]="myProperty || 'any value'"></div>
Enter fullscreen mode Exit fullscreen mode

Since CSS Custom Properties are scoped, this property would be tied to the component’s DOM.

Stable Bazel release as opt-in option

Bazel version 2.1 is available as an opt-in build automation tool for Angular version 9.

How do I get started?

To enable Bazel, either run ng add @angular/bazel or use the @angular/bazel schematics collection when creating a new Angular workspace.

Be sure to consult the Bazel installation guide for your specific operating system.

Angular Components

Angular version 9 ships with official components for YouTube and Google Maps. The Angular CDK also gains a clipboard directive and service.

Testing

A notable highlight of the Angular version 9 release is the substantial testing improvements. Long-standing performance issues are fixed, types are refined, and new concepts are introduced.

Learn about major features and improvements for testing in "Next-level testing in Angular Ivy version 9".

Learn how to create and use your own component harnesses in "Create a component harness for your tests with Angular CDK".

Conclusion

A primary objective has been to maintain as much backwards compatibility as possible between Ivy and View Engine.

Angular version 9 also includes bugfixes, deprecations, and breaking changes. Ivy resolves several long-standing issues that were outside the scope of this article.

Angular Ivy serves as a stepping stone for upcoming features. As covered here, Ivy already offers advantages for various scenarios, but the most exciting developments are anticipated in future Angular releases. Whether these will materialize in versions 10 or 11 remains to be determined.

We’ve only touched on the stable, public APIs in Angular version 9. A few experimental APIs, including renderComponent, markDirty, and detectChanges, are present but still subject to change.

With entry component declarations becoming deprecated and lazy loaded components no longer relying on render modules, we’re moving closer to tree-shakable components and optional Angular modules.

Component features are also available in this release, though they’re exposed solely for internal use by Ivy.

The Angular Ivy version 9 release brings enhancements for bundling, testing, the developer experience, tooling, debugging, and type checking. It’s a robust set of improvements.

Further reading

Lazy loaded components

Watch my presentation “Angular revisited: Tree-shakable components and optional NgModules” to explore render modules in more depth.

Kevin Kreuzer’s piece “Lazy load components in Angular” walks through the mechanics of loading components on demand.

Template type checking

The official Angular documentation on template type checking covers configuration options and how to resolve common template errors.

Globalisation

Manfred Steyer’s “Lazy Loading Locales with Angular” shows how to split locale data by route or feature.

Cédric Exbrayat’s “Internationalization with @angular/localize” explains the Ivy-based localization pipeline.

Additional provider scopes

Christian Kohler’s “Improved Dependency Injection with the new providedIn scopes ‘any’ and ‘platform’” details the use of 'any' and 'platform' scopes.

New debugging API

Consult the official API reference for the complete list of debugging utilities exposed by the framework.

Angular Universal version 9

Two in-depth articles cover the Angular Universal version 9 changes:

  • Mark Pieszak’s “Angular Universal v9: What’s New?”
  • Sam Vloeberghs’ “Angular v9 & Universal: SSR and prerendering out of the box!”

For background on the tooling that shaped these features, Christoph Guttandin’s “Prerender Angular Apps with a single Command” introduces angular-prerender.

CSS Custom Properties binding

Alexey Zuev’s tweet and accompanying demo show CSS Custom Properties bindings in practice.

Acknowledgements

Reviewing technical content always benefits from a fresh set of eyes. This post was reviewed by:

  • Christoph Guttandin
  • Evgeny Fedorenko
  • Santosh Yadav