One Instance to Rule Them All: Singleton Services

Master the art of Dependency Injection in Angular. Learn how to control service scopes, optimize bundles with tree-shaking, and manage global state.

Simulation ProgressStep 1 of 7
0 EXP

Welcome to the Angular Injector Simulation. Let's explore how services are created and shared.

// Initializing Application...
// Waiting for DI system...

The Singleton Pattern in Angular

In software engineering, the Singleton Pattern restricts the instantiation of a class to one "single" instance. In Angular, this is handled automatically by the Dependency Injection (DI) system.

When you register a service with the application's root injector, Angular creates that service once, the first time it is needed. Every subsequent request for that service returns the original instance.

Dependency Check

What is the primary characteristic of a Singleton service in Angular?

Advanced DI Simulations

0 EXP

Apply your knowledge in these advanced simulations. All our content is free!


Achievements

🏗️
Singleton Architect

Successfully configure a root-level service.

🌳
Tree Shaker

Optimize your bundle by using providedIn: 'root'.

🎯
Scope Master

Correctly identify the hierarchy of Angular injectors.

Mission: Configure the Singleton

Edit the code to register the `DataService` as a singleton available throughout the application using the modern syntax.

A.D.A. Feedback:

> Configuration verified. Singleton pattern established.

Challenge: Injector Hierarchy

Arrange the injectors in the order of hierarchy from Top (Global) to Bottom (Local).

Module Injector (Lazy)
Element Injector (Component)
Root Injector (Global)

Challenge: Tree-Shaking Syntax

Fill in the missing code to define a tree-shakable singleton service.

({: })
export class AuthService {}

Consult A.D.A. (Angular Dependency Assistant)

Community Holo-Net

Peer Project Review

Submit your "State Management Service" project for feedback from other Angular Developers.

One Instance to Rule Them All: Mastering Angular Singletons

Dependency Injection (DI) is at the core of Angular. Unlike other frameworks where you might manually instantiate classes (e.g., `new UserService()`), Angular manages these instances for you. The most common and powerful pattern managed by this system is the **Singleton Service**.

What is a Singleton in Angular?

A Singleton service is a service for which a single instance exists in an injector. In Angular apps, there are two main injector hierarchies: the **ModuleInjector** hierarchy and the **ElementInjector** hierarchy.

When we say a service is a "singleton in the app", we usually mean it is provided in the application's **root injector**. This ensures that every component, directive, or pipe that requests this service receives the exact same object reference. This is critical for:

  • **State Management:** Holding user authentication data, shopping cart contents, or UI themes.
  • **Data Caching:** Storing API responses to avoid redundant network requests.
  • **Communication:** Acting as a bus for cross-component communication via Observables/Subjects.

The Evolution: From `providers: []` to `providedIn: 'root'`

Before Angular 6, services were typically registered in the `providers` array of an `NgModule` (usually `AppModule`). While this worked, it had two drawbacks: it was verbose, and it made **tree-shaking** difficult.

✔️ Modern Approach

@Injectable({
  providedIn: 'root'
})
export class MyService {}

Enables tree-shaking. If the service isn't used, it's removed from the bundle.

❌ Legacy Approach

@NgModule({
  providers: [MyService]
})
export class AppModule {}

Service is included in the bundle even if unused.

The "Lazy Loading" Trap

The singleton nature holds true for the root injector. However, if you provide a service in the `providers` array of a **lazy-loaded module**, Angular creates a *child injector* for that module. This results in a **new instance** of the service specifically for that module, separate from the root instance. This is a common source of bugs where data updates in one part of the app aren't reflected in the lazy-loaded section.

Pro Tip: Always default to `providedIn: 'root'`. Only use `providers: []` in modules or components if you explicitly *need* multiple instances or need to override an existing provider for a specific scope.

Angular Dependency Injection Glossary

Dependency Injection (DI)
A design pattern where a class requests dependencies from external sources rather than creating them itself.
Singleton
A service pattern where only one instance of the class exists within a given injector.
providedIn: 'root'
Syntax used in `@Injectable` to register a service with the root application injector, ensuring it is a singleton and allowing tree-shaking.
Root Injector
The application-wide injector. Services provided here are accessible globally.
ElementInjector
Injectors created implicitly at each DOM element. Providing services here (in `@Component.providers`) creates local instances.
Tree-Shaking
A build optimization step that removes unused code from the final bundle. Supported by `providedIn`.
Resolution Modifiers
Decorators like `@Optional()`, `@SkipSelf()`, `@Self()`, and `@Host()` that tell Angular where to look (or stop looking) for a dependency.
Provider
An instruction to the Dependency Injection system on how to obtain a value for a dependency (e.g., `useClass`, `useValue`, `useFactory`).

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of Angular experts, who have years of experience building enterprise applications with advanced DI patterns.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on Angular 16+ specifications.

External Resources

Found an error or have a suggestion? Contact us!