Rendering cycle in Angular applications — browser, angular and zone.js interaction

Modern web stack involves lots of moving parts. Let's take a look at all the actors and their functions in a typical Angular application.

Rendering cycle in Angular applications — browser, angular and zone.js interaction
This article is an excerpt from my Angular deep dive course

Modern web stack involves lots of moving parts. A browser provides DOM to describe what should be rendered on the screen and API to manipulate that presentation. It runs JavaScript as a reaction to some kind of asynchronous events initiated by user actions. The JavaScript code is usually split into framework code and application code. Application code implements business logic that processes input data and updates application state. The task of a framework is to transform the application state into DOM updates. The common name for this phrase is rendering, but it has different names in different frameworks. In Angular it’s known as change detection.

In Angular change detection automatically runs after each asynchronous event. The assumption here is that most events cause application state change that needs to be reflected in the DOM and correspondingly on the screen.

For Angular to know when the application state might change, it needs to know when those events occur. This is where zone.js library comes into play. This library decorates (patches) browser platform’s API so that all asynchronous events in the browser can be intercepted. Angular binds to the hooks exposed by zone.js and uses notifications about DOM events, timeouts, AJAX/XHR, Promise etc. as a cue to run change detection.

Angular doesn’t directly interact with zone.js, but instead uses NgZone, which is a kind of a wrapper around zone.js to restrict scope of the events that Angular should be notified about (more on this in the chapter on zonejs).

Let’s go over each step using the following simple example:

import { Component } from '@angular/core';

  selector: 'app-root',
  template: `
    <div class="container">
        <button (click)="fetchData()">Fetch data</button>
        <div>title: {{title}}</div>
export class AppComponent {
  title = null;

  async fetchData() {
  const response = await fetch('http://example.com/movies.json')
    const todo = await response.json();
    this.title = todo.title;

Here’s what happens when you click on the button:

  1. browser detects the click & adds an event handler to the event queue (browser)
  2. zonejs starts with zoneAwareCallback which simply runs callback registered by Angular
  3. angular executes the wrapper around the callback from a component’s template; the wrapper marks the view and all its ancestors dirty
  4. angular runs fetchData component method through the event listener registered in a component template function
  5. business logic inside fetchData application code runs network request
  6. the request is intercepted by zone.js which schedules macrotask with a browser
  7. the event is processed now, zone.js triggers onMicrotaskEmpty through NgZone
  8. angular reacts by running application wide change detection through ApplicationRef.tick
  9. change detection updates the DOM and runs other side effects

At this point JavaScript yields control to the browser.

The event has been processed, business logic updated application state and Angular updated DOM during change detection. Time for the browser to render the updates on the screen and go on to execute the macrotask related to the nework request.

The browser updates the screen by going through the regular pipeline:

  • [Rendering] Style calculations. This is the process of figuring out which CSS rules apply to which elements based on matching selectors, for example, .headline or .nav > .nav__item. From there, once rules are known, they are applied and the final styles for each element are calculated.
  • [Rendering] Layout. Once the browser knows which rules apply to an element it can begin to calculate how much space it takes up and where it is on screen. The web’s layout model means that one element can affect others, for example the width of the <body> element typically affects its children’s widths and so on all the way up and down the tree, so the process can be quite involved for the browser.
  • [Painting] Paint. Painting is the process of filling in pixels. It involves drawing out text, colors, images, borders, and shadows, essentially every visual part of the elements. The drawing is typically done onto multiple surfaces, often called layers.
  • [Painting] Compositing. Since the parts of the page were drawn into potentially multiple layers they need to be drawn to the screen in the correct order so that the page renders correctly. This is especially important for elements that overlap another, since a mistake could result in one element appearing over the top of another incorrectly.

You can read more about it here.

This diagram describes the interaction between the actors mentioned above.

You can clearly see the operations described above using Chrome Dev Tools profiler. Here’s the callstack for the click handler:

And here are browser tasks after JavaScript yields to the browser:

For more in depth stuff like what you read above check out the course

If you believe something important is missing here do let me know in the comments!

in depth knowledge we trust