The Angular-generated index.html file

A line-by-line examination of the initial HTML markup built by the Angular framework

Alejandro Cuba Ruiz
ngconf

--

During the opening ceremony of the 2012 London Olympic Games, Tim Berners-Lee made a symbolic appearance that recalled his groundbreaking proposal from 1989. Now, over three decades after he laid out those hypertext documents, which included a markup language based on the Standard Generalized Markup Language (SGML), HTML tags continue to use angular brackets to clearly distinguish content from the code that structures it. This decision later influenced the etymology of the Angular framework.

Berners-Lee developed this technology while working at CERN in Geneva, Switzerland, to facilitate the sharing of scientific documents over the Internet, and name it WorldWideWeb.

Tim Berners-Lee during the 2012 Olympics ceremony
Sir Tim Berners-Lee with a NeXT Cube during the 2012 Olympics opening ceremony. Photo credit: http://compmuseum.org/

In addition to the HTML, this computer scientist developed other key technologies that became essential to the Web we know today: the URI, the HTTP application layer protocol, the first web server, and the first web browser.

Nowadays, we have powerful tooling like the Angular CLI to generate a web application scaffolding and modern browsers to interpret from the very initial HTML code to the linked multimedia resources to display or narrate content to our users.

A brief overview of the startup sequence

The typical loading sequence of an Angular application follows these steps — overly simplified to illustrate the process:

  1. The browser sends an initial request to the web server to retrieve the web content.
  2. The server responds with an HTML file.
  3. Upon downloading and parsing this HTML file, the browser discovers the need to fetch additional resources such as fonts, images, and JavaScript code necessary to execute the logic declared in Angular and enable the user’s journey.
  4. The browser sends requests for these additional resources to the server, which may respond with the requested files.
  5. In the meantime, the browser creates and maintains in memory the DOM, a tree structure where each node represents an element in the resulting HTML document. Among them is the <app-root>, which becomes the parent element for the other components in the Angular app.
  6. Once the initial version of the DOM tree is built, the browser uses it to create other models, such as the Accessibility Tree, the CSSOM and the Render Tree, which ultimately calculates, based on content and style information, the layout of the visual elements before they are painted on the screen.
  7. As the user interacts with the application, the Angular framework handles the app logic and updates the user interface as needed.

It all begins with a request for the following text file hosted on a web server:

The default index.html file generated by Angular CLI when creating a new project

Please note that the file content is rendered in your code editor using legible typography and color highlighting to improve readability for developers. When this data is processed from machine to machine, the visual representation of these characters is irrelevant, as the computers only interpret the underlying code, not the Latin symbols themselves. For further insight on this topic, check out the section below on charset encoding.

The line-by-line examination

Let’s take a closer look at the default HTML code generated by the Angular CLI after executing the ng new command.

Line #1: The Doctype

<!doctype html>

This required line with the document type declaration is the first instruction web browsers receive, indicating whether the document is written in a specific version of HTML or XML. This ensures consistency across different browsers and platforms. Without the doctype, legacy browsers may apply incorrect rendering modes, leading to inconsistencies in layout and styling.

Do you recall the complex <!doctype … > declarations in HTML 4.01 and XHTML? When the W3C officially announced the first draft of HTML5 in January 2008, most of us were pleased to discover that the new doctype had been simplified to a single line of case-insensitive code, making the file content cleaner and more readable ever since.

Line #2: The HTML element

Simply put, the <html> element represents the root of the top-level HTML document. All other elements must be its descendants.

We need to declare the natural language of the content by providing the lang attribute with a valid language tag, as defined in RFC 5646. This helps machines, such as search engines and screen readers, to correctly identify the language used in the document content and metadata. Without specifying the language, assistive technologies may default to the operating system's language setting, potentially leading to mispronunciations.

It is worth noting that the lang attribute is not confined to the root element. As a global attribute, it can also be applied to any other element, making it easier for machines to parse multi-language content.

The HTML elements can also contain the dir attribute to indicate the text direction. For content written in languages like Arabic, Farsi, Kurdish, and Hebrew, which use a right-to-left script, the dir attribute can be set to dir="rtl" to ensure proper display.

Line #3: The head of the document

To ensure the web document functions properly and that search engines can understand its content, we should include all necessary metadata in the <head> of an HTML file. This includes style definitions, scripts, and links to required external resources.

Line #4. The machine character set

To display any web document, the browser needs to know how to convert the bytes received from the web server into content that users can understand. The charset definition tells the browser how to decode the downloaded data into legible characters and audible phrases, even if the server does not provide the correct information in the HTTP headers.

Character encoding is the process of assigning numbers to graphical characters, especially the written characters of human language, allowing them to be stored, transmitted, and transformed using digital computers.
- Definition from Wikipedia

UTF-8, which stands for Unicode Transformation Format 8-bit, is the most widely used character encoding on the web. This format was initially proposed by Ken Thompson and Rob Pike in 1992 as a way to represent all possible characters from most human languages. A decade later, it was standardized by the IETF. Although the <meta charset="utf-8"> is typically placed as the first child of the <head> element, you may need to include additional metadata or analytics trackers before this line. Just be mindful to declare the charset definition within the first 1024 bytes of the HTML file to prevent performance degradation. Otherwise, the browser will spend precious milliseconds guessing the charset using other techniques during page loading.

Line #5: The title

Angular allows routing logic to declare the title for each URI separately, rather than just having a static text hardcoded in the <title> element of the index.html file’s . There are multiple reasons why we should always write an unambiguous title per route:

  • UX: It helps users to understand which page or section they are currently viewing, especially when they have multiple browser tabs, windows, or spaces open.
  • SMO and Bookmarking: If users decide to save or share the link corresponding to the current route, a descriptive title can help to quickly grasp the content.
  • SEO and Parsing: Search engines prioritize the <title> element during page indexing, so when someone performs a search related to your web content, a keyword match in the page title may help you to rank higher in the search results. Additionally, the title element is commonly used as the clickable headline.
  • Accessibility: Users with visual impairments or those with a screenless device rely on the web document title to quickly understand the content. A clear title can significantly enhance the experience for anyone using a screen reader or similar technologies.

Line #6: The base root

The <base> element allows us to specify the base URL for all relative routes in a web application. By default, the Angular CLI sets its href property to /, which means that the Angular router will treat the application's root as the domain root. This setup facilitates the resolution of relative URLs for images, styles, scripts, and any other linked resource.

When a web app is deployed in a subdirectory or on a different domain, it may be necessary to update the base URL to prevent the Angular runtime logic from failing to locate required resources due to a misconfigured <base> tag.

Line #7: The browser viewport

In the context of web development, the term "viewport" is captivating because “view” refers to the visible area of a web interface, and “port” can be interpreted as the passage through which the visual content is delivered to the user.

AI-generated image using DreamShaper v8 model

The way we can instruct browsers on how to manage the the layout and scaling of a webpage across diverse screen sizes is by setting specific values in the corresponding meta tag. This is particularly useful for meeting responsive design requirements.

This HTML tag has a content attribute to specify viewport parameters:

  • width and height control the dimensions of the viewport.
  • interactive-widget specify the behavior of overlying UI elements, such as visual keyboards, browser extensions, and accessibility tools.
  • initial-scale, minimum-scale, and maximum-scale set the initial zoom level and restrict further zooming.
  • user-scalable=no prevents user scaling, which obstructs some of the perceivable principles of the WCAG directives.

Line #8: The app icon

The iconic favicon.ico, a staple of the web since the 1990s, paved the way for the modern display of icons that helps to visually identify a website across a variety of devices and platforms. Today, despite the broad support of WebP and the vectorial features of SVG for creating adaptative icons, PNG and JPG remain the most widely used image file formats on the web.

If you choose to augment the default favicon.ico included in every new Angular project by using a fixed bitmap format — which sacrifices pixels and image quality upon resizing — , it's a good idea to rely on the traditional pixel sizes 16x16, 32x32, 192x192, and 512x512 to create the illusion of a fluid icon that appears clear on different media conditions. However, this technique is not very performant due to potential issues with file size, the number of HTTP requests, and perceived image quality.

A better approach to scaling the default Angular icon involves implementing a simple SVG-driven solution. This adaptive method uses the traditional progressive enhancement technique, allowing old browsers to load the ICO or any PNG file from the predefined collection of sizes if they cannot read the SVG format.

<link rel="icon" type="image/x-icon" href="favicon.ico"> <!-- fallback icon -->
<link rel="icon" type="image/svg+xml" href="favicon.svg"> <!-- adaptive icon -->

Line #9, #12, #13: Closing tags

HTML tags are like a boundary: they tell the browser where an element starts and where it ends. Without them, the document markup would probably be a big mess. Closing a tag with the slash syntax ensures that any nested elements are correctly associated with their parent element.

<!-- tags wrapping inner content needs to be closed -->
<element attribute1="value1">Content</element>
<element attribute1="value1"><child>Sub-content</child></element>

Elements containing only attribute values and no inner content don’t require a closing tag. In version 15.1.0, Angular introduced self-closing tags for custom elements without content projection, reducing boilerplate in HTML templates.

<!-- self-closing tag syntax -->
<element attribute1="value1" attribute2="value2" />

Line #10: The body of the document

This required HTML element surrounds the main content of a document. It must be the second child of the <html> tag.

Like the other elements within the DOM, the corresponding HTMLBodyElement interface is an object-oriented representation of the <body> tag. It has various properties and methods that can be used to manipulate and control the element that structures the web page's layout.

Line #11: The root element of the Angular app

The last item on this list corresponds to the <app-root> tag. It is the placeholder for the root element, into which the Angular runtime processes will insert or replace the resulting HTML of the main component after the application completes the bootstrapping phase. Note that you can rename this tag in the main component's decorator declaration to match your preferred naming system.

Beyond the default elements

You may need to add meta tags for SEO and SMO, as well as tracking code for analytics and web performance monitoring. Unless you need to inline specific code, declare external styles and scripts in the Angular configuration file to ensure these resources are loaded efficiently.

Consider including Schema markup — which can be validated using the Google Structured Data Testing Tool — and the <noscript> element to handle uncommon situations where JavaScript is disabled.

Additionally, remember that search engines like the robots meta tag and its companion robots.txt file, web authors humans.txt, and legal departments license.txt. All of them should be located in the root directory.

If you plan to convert your Angular application into a PWA, the Angular CLI automatically updates the index.html file to include the theme color metadata declaration and a link to the manifest.webmanifest file after running the ng add @angular/pwa command.

Try to keep the HTML content to a minimum by including only the elements essential for your product strategy and for Angular to function performantly.

It is also important to ensure that the index.html and any subsequent template files contain valid HTML code, despite browsers traditionally being tolerant of syntax errors. The Angular compiler can help prevent many errors, and tools like the Angular Language Service and ESLint can produce additional feedback during the creation of your markup. This secures the delivery of quality HTML that can withstand various platforms and browser types.

Ready for distribution?

Before wrapping up, take a couple of minutes to execute the ng build command and examine the output of the index.html located in the /dist folder. This file holds the final version of the HTML code that the user's browser will fetch from the web server.

Default index.html file generated by the Angular builder

Notice the few additions that have been made to the source HTML file. For example, the data-critters-container attribute is a byproduct of the Critters library, which inlines the application's critical styles to optimize for better CLS visual stability.

After transpiling the TypeScript code into the targeted version of JavaScript and processing the chosen style syntax into pure CSS, the build system — based either on Webpack or esbuild — minifies and bundles the files configured in the angular.json file.

By default, the Angular framework creates the following linked files with cache-busting hashes included in the filenames:

  • main.js contains the logic of the application, including the runtime code responsible for bootstrapping the Angular application.
  • styles.css contains custom styles that define the overall appearance of the application and its components.
  • polyfill.js, an optional file that facilitates the loading of extra code into the browser. From the early beginnings of the framework, this file was essential for ensuring that modern JavaScript instructions were correctly interpreted in older browsers, but with Angular dropping support for legacy browsers, the default content of this file now includes only the Zone.js library code. Starting with Angular 18, applications experimenting with a Zoneless architecture no longer require this dependency for running change detection, except when there’s a need to temporarily support a hybrid mechanism.

Based on the angular.json file settings and your router-level lazy loading strategy, there could be more files produced that might be either initially declared or loaded on demand.

In server-side rendered apps, where the Angular builder render the initial HTML state of the Angular components on the server, a pre-compiled version of the index.html is generated in the browser distribution directory, along with the client-side-rendered version, the index.csr.html file.

Additionally, in the server directory, the index.server.html file contains a script to manage event dispatches and UI interactions. In this case, the critical CSS inlining is done during server rendering, so Critters doesn’t mark the <html> element in this file as its client-side processing won't happen.

Distinct versions of the index.html file generated by the Angular builder for SSR apps

You might be wondering why the output isn’t minified. This technique, which reduces the HTML file size by removing unnecessary characters like whitespaces and line breaks, was officially discontinued in Angular 6. There is now limited interest in reinstating it in the build process, as the very small performance gains aren't worth the additional work bringing new dependencies to handle markup minification. Also, this topic is particularly relevant in SSR, where HTML must remain untouched—even down to retaining comment nodes— since the hydration mechanism relies on the original markup.

Nevertheless, there's more performance optimization happening for the index.html file, such as CORS support and font inlining. Also, when using the NgOptimizedImage directive, speculative loading hints are added to the <head> of the document to preconnect with custom or third-party loader domains, or even to indicate — in SSR mode — that a priority image to become the LCP element, should preload in the browser to speed up the fetching of critical resources. To discover more automatic adjustments in optimized images, check out this article:

Outro

Understanding each detail of the index.html file — or multiple files in SSR mode — automatically generated at build time, reveals some of the technical design decisions and optimization efforts made by the Angular framework team and contributors.

Inspectors like Chrome DevTools or Firefox Developer Tools can help us examine the resulting DOM structure at various stages of the Angular component lifecycle, application state, and user interactions to optimize our applications for better performance, usability, and discoverability.

--

--

Alejandro Cuba Ruiz
ngconf
Writer for

<front-end web engineer />, Angular GDE, traveler, reader, writer, human being.