Dependency injection (DI) in Angular means a component or service declares what it needs (a service, usually), and Angular's injector creates and hands it the instance — rather than the component creating that dependency itself. The main practical benefit: the same service instance can be shared across everything that depends on it, instead of each consumer creating its own separate copy.
Creating an injectable service
ng generate service data
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class DataService {
getItems() {
return ['a', 'b', 'c'];
}
}
providedIn: 'root' — the modern default
providedIn: 'root' registers the service with the application's root injector, meaning a single shared instance exists for the entire app, and — importantly — Angular's build process can tree-shake the service out entirely if nothing actually imports it. This is why it's the recommended default over listing services manually in a module's providers array.
Constructor injection (the traditional way)
@Component({ selector: 'app-list', templateUrl: './list.component.html' })
export class ListComponent {
constructor(private dataService: DataService) {}
}
Angular sees the DataService type hint in the constructor and automatically supplies an instance — you never call new DataService() yourself.
The inject() function — a newer alternative
Modern Angular (14+) also supports a functional style using inject(), which is especially useful outside of constructors (in route guards, functional interceptors, or field initializers):
import { inject } from '@angular/core';
export class ListComponent {
private dataService = inject(DataService);
}
Both styles register with the same injector and behave identically — inject() is simply more flexible about where it can be called from, not a replacement mechanism.
Component-level providers (scoped instances)
Listing a service in a specific component's own providers array (instead of relying on providedIn: 'root') gives that component and its children their own separate instance, isolated from the rest of the app — useful when state genuinely shouldn't be shared app-wide, like a wizard or form-specific state service.