Skip to content

SCAM: A better way to share components in Angular

SCAM is alternative approach for sharing components between your Angular feature modules. Learn how to apply it and what the benefits are compared to the usual SharedModule approach.

Jul 11, 2021
Jun 03, 2022
Reading time
3 min read

Update June 2022

The article has been improved and includes a section about the new Standalone Components feature of Angular 14.

What is the SharedModule?

A SharedModule, as recommended by the Angular Coding Style Guide, is a single Angular module with generic components, directives and pipes that are shared between feature modules in your Angular application.

Creating a project global place for all common things, like widgets or pipes to transform values, makes sense. You can share them between multiple modules and every developer knows where to put new common components.

The approach also has drawbacks. In general, we want to create small, encapsulated and reusable units. Creating a single shared module may get pretty large as the project grows.

I recommend another approach: SCAM.

What is SCAM?

Single Angular Component Module, or SCAM for short, is the concept of creating Angular Modules with only one component (or directive / pipe). This brings a few benefits.

  • Smaller modules: A SCAM is small and that is what we mostly want: small units of code, like small functions, classes - and also modules.
  • Clear responsibility: You and every other developer in your team will know what that module is responsible for.
  • Clear dependency: You can easily check where your component is used by checking where your module is imported.

You don't have to take "single component" literally, neither "single" nor "component". SCAM also applies to directives and pipes. And it's not about exactly one element. A SCAM may have additional components that are only used internally, or it may even export multiple components that always belong together (like a component for an accordion and a component for an accordion entry). The pattern should bring you benefits and it's not meant to limit you.

SCAM in action

We can apply the SCAM pattern for our SharedModule. We create a shared folder and instead of just one module we create multiple, small, shared modules there. It looks something like this:

└── app/
    └── shared/
        ├── accordion/
        │   ├── accordion.component.css|html|ts
        │   ├── accordion-group.component.css|html|ts
        │   └── accordion.module.ts
        ├── button/
        │   ├── button.component.css|html|ts
        │   └── button.module.ts
        └── some-transformation/
            ├── some-transformation.pipe.ts
            └── some-transformation.module.ts

This example has three shared modules: accordion, button and some-transformation.

Standalone Components

Angular 14 now supports Standalone Components. These components are not part of an NgModule and define their dependencies (like module imports) directly.

  selector: "app-button",
  templateUrl: "./button.component.html",
  standalone: true, // Mark the component as standalone
  imports: [MatButtonModule], // Import other modules
export class ButtonComponent {}

When using the SCAM pattern in an existing project it's easy to migrate to standalone components. You can start using the standalone flag for all new shared components that you create while keeping the module for existing SCAMs. Then you can migrate your existing SCAMs module by module without the need to migrate everything at once.

Tree-shaking and lazy loading

You may ask if the SCAM pattern provides any advantages regarding tree-shaking and lazy loading. The short answer is: no.

The Angular Ivy renderer, that replaces the now deprecated View Engine, has good support for tree-shaking and lazy loading components. It will not include unused components even if they are imported and exported in your SharedModule. In addition, it will move components into lazy loaded chunks even if other components of the same module are used in your main bundle.

Final thoughts

Using shared SCAMs creates some overhead as you have more modules and more imports in your feature modules (unless you are using standalone components). On the other hand, you get smaller, better encapsulated modules that are easier to understand.