Angular EnterpriseAngular Enterprise

Learn how NgModules work

  • Courses
  • 9 practices
  • 6 min read
Learn in 5 minutes how Angular modules work

A lot of engineering work was done by the Google team to develop Angular’s own NgModule system. This system was quite criticized at the beginning of the framework's existence because it is true that it is quite complex. Indeed many beginners in Angular have been repelled by the complexity of these Angular modules. This is why Angular later chose to simplify it slightly via the default providedIn 'root' services and thus avoid more or less serious service lifecycle configuration errors. Finally, with the introduction of standalone components in Angular 14 (stable since version 15 and the default since version 19), NgModules have become optional: new projects are now generated without NgModules, however many projects started before will keep traces of the past even if the Angular team provides an automated migration (ng generate @angular/core:standalone). If you are working on a project with NgModules do not be afraid, the module system is very well designed and with the little learning we will do together it becomes pleasant to use.

Types of modules

  • App module: it's the root module used to bootstrap the application. There should not be a lot of content because most of the codebase must be encapsulated in the Core module and then into domain-specific modules called feature modules.
  • Core module: app-level components like header, footer, breadcrumb, navigation and singleton services. This module should be imported once in app module and the module should not export anything because only the app shell uses those components.
  • Shared module: shared components, directives and pipes used throughout the application must be inside this module and exported to be able to be used by others modules which are importing it. It's common to import and export built-in Angular modules, for instance Common module or Material module inside your Shared Module if you really need to access them in multiple locations like in many Feature modules. In order to optimize your app at best this module should not be imported in App or Core modules, this way it will be lazy loaded once the user starts using the first lazy loaded feature module. See below one shared module vs multiple shared modules. And because your Shared module(s) will be imported by many lazy loaded features they must only contain declarables and modules which only contain declarables and must never implement any services via the providers array else you will have issues with the singletons.
  • Feature module: Dividing things into domain-specific, lazy-loaded modules will help your project in the long run. These modules must be isolated and must not export anything other than the module itself to perform lazy loading.

Lazy modules

Understanding the concept of lazy modules is important when developing an Angular application, indeed this pattern brings many advantages.

  • Faster application boot time, indeed if you split your app into multiple lazy loaded feature modules the main bundle will stay small and thus your app will be fast to load and boot.
  • Faster rebuilds in dev mode, indeed during development it is also faster since the compiler has only to rebuild the lazy modules affected by your changes and not the whole application.
  • Feature isolation helps your app maintainability and also guarantees a better scalability since your code is easier to understand and work on.

One shared module

  • If we put all shared pipes, directives and common components in one big shared module and then import it everywhere (inside sync and async chunks) then this code will be found after compilation in our initial main chunk. So this is not the recommended method especially if your Shared module contains a lot of things.

Multiple shared modules

  • On the other hand, if we split commonly used code across lazy loaded modules then a new shared chunk will be created and will be loaded only if any of those lazy modules are loaded. This should improve the application initial load. But do it wisely because sometimes it’s better to put small directives in one chunk than having the extra request needed for a separate tiny chunk load.

Module configuration

Declarations

The declarations array only takes declarables. Declarables are components, directives and pipes. Declarables must belong to exactly one module else the compiler emits an error. Declarables are private by default so they can be used only in the template of any component that is part of this same NgModule, if you want to use them outside you must use the exports array. Pipes are a special case: if you want to inject a pipe in a class in order to call its transform function from TypeScript code, it must also be added to a providers array. You can provide your pipe locally in the component, or use providedIn: 'root' in an @Injectable() decorator if you want to share it globally as a singleton.

Imports

Other modules whose exported declarables are needed by component templates declared in this NgModule. Basically this is useful in order to use declarables that are exported in others modules. For instance if ModuleA imports ModuleB then ModuleA will be allowed to use any declarable exported by ModuleB.

Exports

The subset of declarations that should be visible and usable in the component templates of other NgModules. It means that a template external to this module can use exported declarables from any imported module. For instance if you want to use in your app template a modal component declared in a modal module then this modal component should be declared and exported in your modal module first and of course modal module should be imported in your app module. Also another more complex case is the indirect export principles made possible by exporting a whole module instead of a single declarable. Indeed if ModuleA imports ModuleB, and also exports it, this makes the declarables from ModuleB available wherever ModuleA is imported.

Providers

The providers array is part of the dependency injection, you will learn more about this part in this dependency injection course.

Up next

Learn more about Angular