Here’s why you should be excited about Ivy!

An overview of the features Ivy brings us today, and a look at possible features Ivy enables in future versions of Angular.

Kevin Kreuzer
Feb 4 · 13 min read

Recently, during coffee, I had a chat with a developer about Angular 9. I told him how excited I am for the upcoming release. In particular, because of Ivy.

Even though he already played around with Ivy in version 8, he couldn’t understand my excitement.

“All that hype for a bit smaller bundles!”

Well, there’s a lot more to it. Let’s explore why I am excited about Ivy and why you should be too?

Yes, we get smaller bundles

Let’s start with the feature most developers are aware of. With Ivy, we get smaller bundles. At last year’s ng-conf, the Angular team presented some statistics that show how Ivy reduces bundle size compared to the ViewEngine.

Bundle size statistics presented at the last ng-conf

Switching to Ivy, we get smaller bundles for free! Pretty cool. But how come? Well. Because we call Ivy!

The big idea behind Ivy: Inverting

Ivy is quite different from the ViewEngine.

The ViewEngine takes all the Angular features and iterates our code through. Based on the features we use, Angular executes the required framework code.

Ivy inverts the behavior of the ViewEngine.

Ivy, on the other hand, compiles our HTML into JavaScript instructions, which pull in the Angular capabilities.

The ViewEngine calls us; we call Ivy

Let’s analyze this behavior further by having a look at an Ivy compiled template of an Angular component.

Ivy keeps our Component code and adds a factory to it. Furthermore, it adds all the necessary component metadata like selectors, inputs, outputs etc. All metadata remain local to the component (Locality).

The most exciting part, however, lies in the template function. The template function renders our DOM. It contains two phases, which are represented by the rf parameter. In the first phase, the template is built up, the elements are written into the page. In the second phase, the template bindings are resolved.

The code inside the template function creates the DOM by calling Ivy instructions. This significant change over the ViewEngine is the one that improves our bundle size. How come?

“If you want to enable tree shaking you need to be explicit about your intents”

By executing Ivy instructions, our code is very explicit about it’s intends and can, therefore, be analyzed by the tree shaker. The tree shaker now knows which parts of Angular we are going to use and which not.

Thanks to this change, the tree shaking mechanism can shake out the unsued parts. In a nutshell, Angular itself becomes tree shakable.

Okay, that’s cool. But so far, we only saw bundle size improvement. (Which, by the way, in my opinion, is already enough to be pretty excited about Ivy! 😉)

Lazy loading components with Ivy 🌱

Another great feature Ivy offers us today is; lazy loading a component.

Lazy loading a component? 🤔 You mean, lazy loading modules, didn’t you? No, I mean lazy loading components.

In current Angular versions, modules can be lazy-loaded with the help of the Angular router. Once a user hits a lazy loaded route, Angular loads the required chunk of code in a separate request.

Currently, it’s only possible to lazy load on a module level. With Ivy, we get more fine granular control over lazy loading.

Imagine we have a Quiz application. We want to dynamically load the QuizCardComponent once the user starts the Quiz.

lazyload a component and dynamically add it to a ViewContainer

Once the user clicks on “Start quiz” we go on and fetch our QuizCardComponent. Once loaded, we dynamically add the component to a container. From here on, we can interact with the component.

With Ivy, lazy loading a component can be achieved by using the EcmaScript import syntax.

The simplest way to lazy load a component in Angular 9 with Ivy

The import statement returns us a Promise. Once this Promise resolves, we get a hold of the component instance.

That’s the overall idea. Of course, with the snippet above, you can’t yet implement a productive lazy-loaded component. There’s more to it especially once you start to use third party components like Angular material.

Don’t worry. If you want to find out how to write a productive lazy-loaded component that uses Angular material, check out my article on “Lazy load components in Angular”.

Maybe you think; Pretty cool. But how often will I use this feature? Lazy loading on a module level is already pretty good.

Okay. I see you are not yet thoroughly excited. Let’s have a look at the next feature Ivy offers us today.

Improved debugging — the flying ng

Thanks to the way Ivy compiles our templates, debugging support has improved. Especially debugging experience in our browser.

Ivy exposes an ng object. This object “flies” around in our console. We can use it at runtime to get a component instance. Once we got hold of a specific component instance, we can start to manipulate it.

To do so, we open up our DOM explorer and use the arrow symbol on the top left corner of our dev tools to select the desired component.

We can use $0 to get a reference to the current DOM node. By using ng.getComponent($0) we get a hold of our component.

Use the ng object to manipulate components at runtime

In this Quiz game, the user has to guess the city. The displayed city above is Bern. But we manipulate our QuizCardComponent with the ng tools and change the correctAnswer on the QuizCardComponent to “Dublin”.

When we click on Bern, we can see that the QuizCardComponent shows a red color to indicated that our answer was wrong, and it displays the correct answer, Dublin (which is not correct).

We can manipulate our components at runtime in the browser! Pretty nice. Are you already excited about Ivy? No? The best is yet to come! The possibilities Ivy offers for the future!

So far, we talked about features that we can already use today. In my opinion, the most significant thing about Ivy is the possibilities it offers for the future.

Ivy is the enabler for the future of Angular

⚠️ Warning! All the APIs used in the examples are private or so-called “thrill seeker” APIs. They aren’t yet officially supported APIs. Their future is unclear. They may or may not land in future versions of Angular.

ɵrenderComponent

Remember the bundle size image at the beginning of this blogpost? Maybe you were wondering how we can get our bundle size to 14KB and what does “Thrill seekers” API mean?

A “Thrill seekers” API is an API that is experimental and not yet finalized. Its also not yet clear if it will be finalized in future versions or not. All thrill seeker APIs in Angular use a delta prefix (ɵ).

Even though the “thrill-seekers” APIs aren’t yet finalized, we can already use them and play around with them. Since they contain a delta prefix(ɵ), which, by the way, nobody knows how to type on their keyboard, 😉 I recommend using an import alias.

import {ɵrenderComponent as renderComponent} from '@angular/core';

Ok. Let’s have a look at what we can do with renderComponent. Well, we can render a component. 😊

Usually, we start our Angular application with the bootstrapModule function. Let’s comment on the current bootstrap approach and use renderComponent instead.

Bootstrapping of a single component without the usage of a Module

If we run ng serve we won’t notice a difference; everything works as before. Even without a NgModule. A considerable difference though lies in our bundle size which now is down to 23 KB. Gzipped we would even land under 10 KB.

renderComponent is the way to achieve really small bundle sizes.

But how does renderComponent know where to render our AppComponent?

Well, we still have a selector in our DOM, we are using the <app-root> tag in our index.html. If we have such a selectorrenderComponent renders the component directly to the selector.

ɵrenderComponent in combination with lazy loading

Render component is also convenient in other use cases. For example, with lazy loading components.

lazyload a component and render it using renderComponent

renderComponent accepts an additional opts property. Those options allow us to pass the host where our component should be attached and also an injector so that our component can take advantage of DI.

This is important when we lazy load our component. In a lazy-loaded scenario, we don’t have a selector in our DOM; therefore we need the host property.

ɵrenderComponent for Web components

renderComponent gives us new power when it comes to the creation of web components. It allows us to write tiny web components around 10 KB of bundles size. Which is a suitable size for most use cases.

Let’s quickly have a look at how we could deliver a simple switch as a web component.

Simple switch component

To deliver this component as a web component, Ivy allows us to write an element’s class for this.

Element wrapper for the switch component

The SliderElement class extends HTMLElement. Once the constructor of this class is executed, we use the renderComponent method to tell Ivy that it should render our SliderComponent to this CustomElement.

The renderComponent method returns us a component instance. We use this instance to react on @Outputs or to pass @Inputs to our component. Passing Inputs is straight forward, however, when we react on @Outputs we subscribe to the stream and use the dispatchEvent method to dispatch a CustomEvent.

Once we have set up our SliderElement we still need to define it as a custom element in our main.ts.

customElements.define('my-slider-element', SliderElement);

Great! We are now able to interact with our Web component using plain HTML. If we can interact with a component using standard DOM APIs, we are guaranteed that we can also interact with those components using other frameworks.

Interact with a custom element using standard DOM APIs

What about NgModules? 📦

Modules are an essential part of every Angular application. With the ViewEngine, they contain all the necessary metadata for a component to exist. However, in the previous example, we were able to render a component without the usage of a module. Do modules become optional?

To answer this question, let’s have a look at a component compiled with Ivy.

Ivy metadata uses directives to specify dependencies to other components

Earlier, we have seen that Ivy introduces a concept called locality. This means all the metadata are local to the component. In other words, Ivy does not care about NgModule. Ivy uses the directives metadata to know about the dependencies of a component.

directives: [_hello_component__WEBPACK_IMPORTED_MODULE_1__["HelloComponent"]],

Currently, Ivy knows about this because it extracts the information from the NgModule. In future versions, however, Angular could have a public API to declare dependencies directly on the component. There are multiple proposals out there.

1. Grouping components in an array

One idea is to specify all the components in an array and then use the spread operator to add them to the components directives array.

Directly assign the dependencies to the components directives property.

Lars Gyrup Brink Nielsen gave a great workshop about optional NgModules. I recommend you to check it out:

Specify Deps on the component

Minko Gechev from the Angular team proposed a solution that allows us to specify the dependencies on the component decorator.

Specify the dependencies of a component on the components decorator

Again, those are proposals, and it’s not yet sure if and how they land in Angular.

Higher-order components

The “higher-order” concept is something we already know from other use cases, for example, higher-order Observables or higher-order functions.

Higher-order functions are probably the easiest way to explain this concept:

Higher-order functions are functions that take other functions as arguments or return functions as their results.

A well-known example of a higher-order function is the map function on arrays. If we would implement the map function on our own, we perform the mapping according to a projection function which we accept as a parameter.

Custom implementation of a map function

Higher-order functions allow us to use composition. We can create multiple small functions and compose them to a more complex function.

The same concept applies to components. Higher-order components are components that return new components. Let’s have a look at a use case where higher-order components would make sense.

Let’s start with a dumb component that displays a name.

A dumb component that displays a name

It’s a dumb component because it does not care where the name comes from, it only shows it.

Dumb components are an essential part of every Angular application. They only care about displaying @Inputs and emitting @Outputs. But alone they are not very useful. Usually, dumb components are used inside a smart component.

What if we want to use the HelloComponent in multiple places where it would need to access the name from the QueryParams in the route. Maybe we even have a PriceComponent which does the same but obtains the price from the price QueryParam instead of the name QueryParam. We would need to write a lot of duplicated code.

The idea of a higher order component can help us here.

Pseudocode that illustrates the idea of a higher-order component

We have a function called displayQueryParam which accepts a component. Inside this function, we create our higher-order component. In the ngOnInit life cycle hook of this component, we read all the QueryParams.

The most exciting part is then happening inside the template function. In the template function, we would dynamically create an instance of the component we passed to the displayQueryParam method. Next, we would use the component's metadata to check if one of the QueryParams matches an @Input property. If so, we would pass that value to the Component.

Since this is not officially supported, writing this code is currently very complicated. But we don’t want to dive too deep into details; I just want to show the concept.

Once we implemented our HigherOrderComponent and the displayQueryParma function, we could use it in our routes.

usage of a function that returns a higher-order component in your routes definitions

This would render the HelloComponent and display the name property from the route. With the PriceComponent it would display the price property from the QueryParams.

Zone less change detection

Currently, Zone.js is an integral part of automatic change detection. Even though it’s not needed for Change Detection itself. Zone js monkey patches asynchronous browser events. Once such an event occurs, it notifies Angular to run change detection.

However, Zone.js, has a couple of downsides. First, it's around 100 KB of size. This is a show stopper for Web components. Even though we can shrink our bundle to 10 KB size, you would still need to ship 100 KB of Zone.js for Change detection to work. The other big downside is that Zone.js can not patch native async/await statements.

How does Ivy help?

Well, Ivy introduces a markDirty function. The markDirty function takes a component as a reference. It then marks this component as dirty and causes change detection to run. The markDirty function also ensures that change detection doesn’t run too often.

Push pipe

“Zone-less” approaches take advantage of the markDirty API. One proposal is to implement a push pipe which would be very similar to the already known async pipe. The difference is that under the hood it uses markDirty instead of markForCheck.

{{ someStream$ | push }}

Reactive Components

Mike Ryan gave a great talk at last year’s AngularConnect in London. He dived deep into the markDirty function and introduced the idea of a ReactiveComponent, which runs Change detection once a stream emits a value.

Zone-less change detection with reactive components

Extending the ReactiveComponent would give us aconnect method to run Change Detection once our stream emits a value.

Conclusion

Ivy is the most exciting thing in the upcoming Angular release. Ivy offers us a lot of great features we can already use with Angular 9.

  • Smaller bundles out of the box
  • Lazy loading components
  • Improved debugging in the browser

Even more exciting than the features we can already use today is the view in the future. Ivy enables a lot of great things for the future of Angular. Some may make it to Angular, and some may not. It’s yet unclear. But Ivy offers the potential for the following features.

  • renderComponent — for tiny bundles
  • Tiny web components with the help of renderComponent
  • Optional NgModules
  • Higher-order components
  • Zone-less change detection

You can already play around with the thrill-seekers APIs. But don’t use them for production. We don’t know if those features will make it to Angular.

🧞‍ 🙏 If you liked this post, share it and give some claps👏🏻 by clicking multiple times on the clap button on the left side.

Claps help other people to discover content and motivate me to write more 😉

Feel free to check out some of my other articles about front-end development.

Kevin Kreuzer

Written by

Passionate freelance frontend engineer. ❤️ Always eager to learn, share and expand knowledge.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade