Standalone Components: A Guide For Building More Scalable Applications
Why Do We Use NgModules?
In the summer of 2016, with the release date for Angular 2 looming, feedback from developers testing the release candidate was lukewarm at best. Two main problems emerged:
- The library story, which involved publishing reusable Angular libraries via npm, was broken. This resulted in authors getting unwanted and buggy results on their Angular libraries.
- Specifying fine-grained dependencies was error-prone and tedious and grouping them was a daily challenge. As a workaround, developers had to import large arrays of dependencies.
To address these issues, NgModules were introduced. They made grouping, scoping, and encapsulating components easier. However, since their inception, the creators of Angular have been wanting to make NgModules optional due to their boilerplate code and complexity, which can be overwhelming for newcomers who must learn a new module system separate from ES6 modules.
Igor Minar, one of the co-creators of Angular, officially revealed these intentions in an insightful Twitter thread.
What Makes Standalone Components Possible?
Ivy, the next-generation compiler for Angular, became the default compiler with Angular 9. One of its defining features is locality, which involves compiling components individually and including all dependencies within the component, eliminating the need for external modules.
For more information about Ivy, you can refer to Angular Minds.
Standalone Components
Regular components, directives, and pipes are constituents of a larger NgModule and are only available within the components declared in that module, which also provides the dependencies for them.
Services, on the other hand, are provided in three different ways:
- Directly in the component providers array (Component scope).
- In the NgModule providers array (NgModule scope).
- By the service itself with
providedIn{}
(you provide the scope). We use this method by default as of Angular 6.
Standalone components get rid of the module layer, creating an independent entity to declare their own dependencies:
Bootstrapping
When launching an Angular application, the index.html file is loaded, and it references the root component. While this process may seem simple, there are two crucial steps that take place:
- In the main.ts file, you need to specify which NgModule to bootstrap. By default, this is usually the AppModule.
- Within that NgModule, you need to specify what component to bootstrap. By default, this is usually the AppComponent.
However, it is possible to bootstrap a standalone component as well. Thanks to the bootstrapApplication API, you can bootstrap a component directly without having to reference an NgModule. This makes it possible to create an entire application without NgModules.
Routing and Lazy Loading
When it comes to configuring routes in Angular, the RouterModule is our go-to. This module can be imported into the root module to define root-level routes with an array of routes containing the path/component sets. We then pass this array to the forRoot(routes) method. For child routes, feature modules define module-specific routes using the forChild(routes) method.
When a user navigates to a particular URL path, the RouterModule uses the router’s configuration to determine which component should be loaded and displayed. It is also possible to specify if these routes should be lazy-loaded, meaning the components are only loaded when the route is requested instead of being loaded when the app initializes.
It is worth noting that when using standalone components, we no longer need to import the RouterModule. Instead, we can pass the routes array to the providers array when bootstrapping the application using the provideRouter(appRoutes) method. This also eliminates the need for the forRoot(routes) and forChild(routes) logic:
Conclusion
In conclusion, the benefits of Standalone Components make them a compelling option for developers looking to create more efficient and streamlined Angular applications. While NgModules may have solved initial issues when Angular 2 was launched, Standalone Components require less code and are easier to understand. Not relying on NgModules can also result in faster and lighter builds since the compiler has fewer files to process.
Moreover, adopting a component-first architecture with Standalone Components can prepare developers for different architectural approaches in the future. However, that is a topic for another article. In short, Standalone Components is not only a more efficient and effective option, they may well be the future of Angular development.