Blog

Standalone Components: Angular Development Efficiency

Category
Software development
Standalone Components: Angular Development Efficiency

Beginning with Angular version 14, it is now possible to develop a complete application without creating any custom NgModule. Angular provides the option to utilize standalone components, which significantly streamlines the application development process.

What is It All About?

With the implementation of standalone components, creating one without the need to declare and export it through a module becomes practical. Other components can directly import such a component, and it can import its own dependencies. This principle can also extend to directives and pipes. Therefore, we can consider this a new feature from the Angular team.

While they introduced standalone components, that doesn’t indicate the end of Angular modules (ngModule). In fact, these components you may also know as “optional modules“, suggesting that we can still utilize modules.

In this blog post, we will demonstrate (step by step) how to create a simple Angular standalone component. Which, furthermore, has the ability to import a module and vice versa. As a result, both ngModules and standalone components can exist within the same codebase.

Defining Standalone Components

By adding the –standalone flag to the ng generate component command, you have the ability to produce a self-contained module, pipeline, or instruction.

  • ng generate pipe unit --standalone
  • ng generate directive red-color --standalone
  • ng generate component sign-in --standalone

Suppose you want to create all components/directive/pipes as standalone. Adding “–standalone” to every single Angular CLI command could become tedious. In this case, you can modify the angular.json file located in the root directory of your project. You may edit the existing schema or add the following to it:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
                "angular-standalone": {
                "projectType": "application",
                "schematics": {
              	             	"@schematics/angular:component": {
              	            	"style": "scss",
              	            	"standalone": true
              	             	}
                },
                "root": "",
                .....
                }
  }
}Code language: JavaScript (javascript)

However, despite its simplicity, it is worth considering what this means for our architecture. What are the implications of this new feature, and why should we take note? What are the benefits of the new Angular approach with standalone components? 

Let’s see how they work in some real-world examples.

Using an Application

To demonstrate this feature, let’s create a simple Angular application using the current latest version, Angular 15.

  • ng new angular-standalone

To create AppComponent as a standalone component, we only need to mark it as standalone. Furthermore, for our application to function correctly, we must import the RouterModule.

import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
 
import { NavbarComponent } from './ui/navbar/navbar.component';
 
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterModule, NavbarComponent],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {}Code language: JavaScript (javascript)

Routing

Since we are not using any additional modules, we will generate a folder named ‘ui‘. Within this folder, we will produce three standalone/autonomous components (Navbar, Flights, and My Flights) using an ng command that employs an Angular schematic.

To create standalone components, we must add a flag at the end of the command (if you added standalone to true in your angular.json, you don’t need to add that flag). In AppComponent, we import RouterModule, so we must also add a router outlet for routing inside the application. 

<app-navbar></app-navbar>
<div class="main-content">
  <router-outlet></router-outlet>
</div>Code language: HTML, XML (xml)

The next step is to create a routing file inside the ‘flights’ & ‘my-flights’ folders to define routing for the standalone components. This is the main difference between the ‘old way’, where we used modules, and the ‘new way’ introduced by the Angular team.

Essentially, everything remains the same, except we no longer have a module.

Flight.routing.ts file:

import { Routes } from '@angular/router';
 
export const FLIGHTS_ROUTES: Routes = [
  {
	path: '',
	loadComponent: () =>
  	import('./flights.component').then((c) => c.FlightsComponent),
  },
  {
	path: ':id',
	loadComponent: () =>
  	import('./flight-details/flight-details.component').then(
    	(m) => m.FlightDetailsComponent
  	),
  },
];Code language: JavaScript (javascript)

App.Routing.ts file:

import { Routes } from '@angular/router';
 
export const APP_ROUTES: Routes = [
  {
	path: '',
	pathMatch: 'full',
	redirectTo: 'flights',
  },
  {
	path: 'flights',
	loadChildren: () =>
  	import('./ui/flights/flights.routing').then((m) => m.FLIGHTS_ROUTES),
  },
  {
	path: 'my-flights',
	loadComponent: () =>
  	import('./ui/my-flights/my-flights.component').then(
    	(m) => m.MyFlightsComponent
  	),
  },
];Code language: JavaScript (javascript)

Afterward, you need to remove app.module.ts, and substitute the bootstrapModule() located in main.ts with the bootstrapApplication(). Additionally, all routes we use in the application we must define and import in main.ts.

import { importProvidersFrom } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { HttpClientModule } from '@angular/common/http';
import { provideRouter } from '@angular/router';
import { APP_ROUTES } from './app/app.routing';
 
bootstrapApplication(AppComponent, {
  providers: [importProvidersFrom(HttpClientModule), provideRouter(APP_ROUTES)],
}).catch((err) => console.error(err));Code language: JavaScript (javascript)

Performance Breakdown

To compare performance, we will create an identical project we wrote using Angular standalone components. We will also create it using modules (following the old approach of writing code). 

An example is a simple application without any business logic, so it is very difficult to compare performances. With that said, I’ll use a straightforward way to compare Angular applications with standalone components and without. I will create two projects to compare their performance. One project will use ngModule to organise its components into modules, while the other will use standalone components. The project structure will be slightly different, and you can find more details in their respective repositories.

In that case, the structure of the application will be slightly different, and you can see the same at the following Github links:

All that we know how to do is to run the following command for build in each application:

ng build

Angular Application Without Standalone Components 

Angular application without standalone components

Angular Application With Standalone Components 

Angular application with standalone components

It’s Not All About Numbers

First, let’s see what the definition of lazy loading is:

“Lazy loading is a technique used in software development to optimize the performance of an application by loading only the required resources and components at runtime, rather than loading everything upfront.”

Results for initial chunk files are almost the same. We can’t see any benefits here. But you can see great results on component-level code-splitting. In the first image, you can see three loaded files.

But check out this second image! You’ll notice that it uses a super cool technique called “component-level code-splitting” to implement lazy loading.

Moreover, separating the code for standalone components into their module using lazy loading can help reduce the size of the application bundle. This approach is especially helpful for large standalone components with a significant amount of code, as it ensures that the code is loaded only when needed. It can also help simplify the codebase by breaking it into smaller, more manageable modules. This makes it easier for developers to navigate and maintain the codebase over time, resulting in a more efficient and productive development process.

An alternative method for measuring application load performance is through runtime performance. Runtime performance refers to the amount of time it takes for a browser to download the application, load it into the browser, parse the JavaScript, and display it to the user.

Levels of Runtime Improvements

It’s hard to track the runtime result (especially on such a simple application). But there are a lot of benefits for runtime using standalone components.

Multiple factors can influence the performance of an Angular application during runtime. One of which is the use of standalone components. When an Angular application is built with standalone components, each component is loaded separately as required. As a result, this can enhance runtime performance by reducing load times since only the specific components necessary for a task or page are loaded instead of loading the entire application all at once.

On the other hand, if an Angular application doesn’t use standalone components, all components may be loaded simultaneously, including those that aren’t required for a particular task or page. This can lead to slower load times and, overall, slower overall runtime performance.

Why Use Standalone Components?

Angular developers can take advantage of several benefits by using standalone components:

  • Reusability: they can be reused throughout an application, saving time and effort by reducing the need for code duplication.
  • Isolation: they are isolated from other parts of the application, making them simpler to test and debug. This reduces the risk of introducing bugs or errors.
  • Encapsulation: they encapsulate their own styles and templates, reducing interference with other components and maintaining a clean codebase.
  • Modularity: they promote modular development, making it easier to break down large applications into smaller, more manageable pieces.
  • Development efficiency: they can be developed and tested independently, streamlining the development process and reducing the likelihood of errors.

Conclusion

Developers can increase the efficiency and scalability of Angular applications by utilizing standalone components, smaller and independent modules that can be easily reused in different parts of an application. Using standalone components, developers can improve runtime performance and reduce load times, leading to a more responsive and efficient application. Additionally, standalone components promote code reusability and maintainability, making it easier to modify and update code over time.

By reducing redundant code and improving the overall structure of an application, developers can streamline the development process and create more successful projects. If you’re interested in exploring standalone components further, you can check out the examples on GitHub (Angular Standalone, Angular Without Standalone). Clone the application, install the npm dependencies, and start up the JSON server. You can experiment with standalone components and see the improvements for yourself.

Overall, standalone components represent a significant advancement in Angular development, and it will be exciting to see what future enhancements will bring to this already impressive platform.

CONTACT US

Exceptional ideas need experienced partners.