LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2516 min read

Providers & Injection Tokens

Control how and where a dependency is provided using providers arrays, useValue/useClass, and custom injection tokens.

Providing at Different Levels

providedIn: 'root' gives you one app-wide singleton, but a service (or any dependency) can also be provided at a narrower scope — a specific component and its children — giving that subtree its own separate instance.

Component-Level Providers

Adding a service to a component's own providers array creates a new instance scoped to that component and its descendants, instead of sharing the app-wide singleton.

import { Component } from '@angular/core';
import { CartService } from './cart.service';
@Component({
selector: 'app-checkout',
standalone: true,
providers: [CartService], // a fresh instance, just for this component subtree
template: `...`,
})
export class CheckoutComponent {}
When This Matters

Component-level providers are useful when a piece of state should reset each time a component is created — like a multi-step wizard's in-progress form data, which shouldn't persist once the wizard closes.

useValue, useClass, useFactory

Beyond providing a class directly, Angular's provider syntax supports swapping in a plain value, a different class implementing the same interface, or a factory function — useful for configuration and testing.

providers: [
{ provide: ApiUrlToken, useValue: 'https://api.example.com' },
{ provide: LoggerService, useClass: ConsoleLoggerService },
{ provide: FeatureFlagService, useFactory: () => new FeatureFlagService(loadFlagsFromConfig()) },
]

Injection Tokens

For providing something that isn't a class — a plain configuration object or string — an InjectionToken gives Angular's DI a unique, type-safe key to provide and inject it by.

// api-config.token.ts
import { InjectionToken } from '@angular/core';
export interface ApiConfig {
baseUrl: string;
}
export const API_CONFIG = new InjectionToken<ApiConfig>('API_CONFIG');
// app.config.ts
providers: [
{ provide: API_CONFIG, useValue: { baseUrl: 'https://api.example.com' } },
]
// Using it
import { inject } from '@angular/core';
import { API_CONFIG } from './api-config.token';
export class ApiService {
private config = inject(API_CONFIG);
getUsers() {
return fetch(`${this.config.baseUrl}/users`);
}
}

FAQs

When you want a different implementation for a given token — for example, providing a MockPaymentService in tests wherever a component expects PaymentService.

Configuration is the most common use, but they're also useful whenever you want to inject something Angular can't identify by class alone (like a plain interface or a primitive value).

Summary

Providers control not just what gets injected, but at what scope and with what actual implementation — flexibility that becomes valuable as an app and its test suite grow. Next, you'll use a service to talk to a real backend with HttpClient.

Next Lesson →

HttpClient & Fetching Data