Chapter 5.4☕ 14 min read

Providers — Root, Component, Module Level

Providers tell Angular HOW to create a service instance. Understand the different levels and when to use each.

01What are Providers

Providers tell Angular HOW to create a service instance. Think of a provider as a recipe — when someone asks for a service, Angular looks at the provider to know how to make it.

In modern Angular (17+), you rarely write providers manually. The @Injectable decorator handles most cases. But understanding providers is essential for advanced scenarios.

02providedIn: root — App-Wide Singleton (95% of cases)

providedIn: 'root' is the most common and recommended way to provide services. It creates ONE instance for the entire application.

Key benefits:

  • Singleton — same instance everywhere
  • Tree-shakeable — if no component injects it, Angular removes it from the build
  • No manual registration — works without adding to any providers array

Use this for 95% of your services — API services, auth, logging, state management.

03Component-Level Providers — Component-Specific Instance

Sometimes you need each component instance to have its OWN service instance — not share it. Add providers: [] to the component decorator.

Use cases:

  • Each tab/accordion needs its own state
  • Each user profile card manages its own edit state
  • Form wizard steps that should not share intermediate state

Child components of a component with providers will get the SAME service instance (unless they also provide their own).

04Provider Configuration Objects

Beyond simple class providers, Angular supports several provider configurations for advanced scenarios:

  • useClass — substitute one class for another: { provide: LoggerService, useClass: ConsoleLoggerService }
  • useExisting — alias an existing provider: { provide: LoggerService, useExisting: ConsoleLoggerService }
  • useFactory — create instance with custom logic, includes deps: [] for dependencies
  • useValue — provide a direct value (string, object, etc.): { provide: API_URL, useValue: 'https://api.com' }

The pattern is: provide: TOKEN, use*: VALUE. What you provide and how you create it are separate concerns.

05InjectionToken — For Non-Class Dependencies

InjectionToken is for when you want to inject something that is NOT a class — strings, numbers, objects, configuration.

Why InjectionToken instead of string? Using a string token can conflict if another library uses the same string. InjectionToken creates a unique token with no naming collisions.

Common use cases: API base URLs, app configuration objects, feature flags, environment settings.

Key Takeaways

  • ✅ providedIn: 'root' creates an app-wide singleton (use for 95% of services)
  • ✅ Component-level providers (providers: []) create a new instance per component
  • ✅ Provider configs: useClass, useExisting, useFactory, useValue for different scenarios
  • ✅ InjectionToken provides unique, collision-free tokens for non-class dependencies
  • ✅ Root services are tree-shakeable — removed from build if unused
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase