If youâve been working with Angular for a while, youâve likely seen this in component metadata:
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
providers: [MyService],
// or
viewProviders: [MyService]
})
At first glance, both providers and viewProviders seem identical.
They both inject services into a component, right?
So why did Angular create two separate options?
The difference lies in scope, encapsulation, and how Angular renders views and projected content.
Letâs take a deep dive into how these two work, why they exist, and how to choose the right one in your project.
đ§ Quick Refresher: How Dependency Injection Works in Angular
Angularâs Dependency Injection (DI) is like a smart factory that creates and shares services across your app.
Instead of manually creating new instances like:
const userService = new UserService();
you simply ask Angular to give you one:
constructor(private userService: UserService) {}
Angular then looks through a hierarchical injector tree to find (or create) that service instance.
Where you register that service (root, module, component) decides how many instances exist and who can use them.
đïž What Are Providers?
A provider is a set of instructions that tells Angularâs DI system how to obtain a value for a dependency token â usually a class, object, or value.
You can define providers at multiple levels:
root (global singleton)
NgModule (shared within a module)
Component (scoped to the component and its child tree)
Example:
@Component({
selector: 'app-parent',
template: `<app-child></app-child>`,
providers: [MyService]
})
export class ParentComponent {
constructor(private myService: MyService) {
console.log('Parent service instance:', myService);
}
}
Hereâs what happens:
Angular creates a new instance of MyService specifically for ParentComponent.
All child components inside its view (like ) get the same instance.
But components outside of this parent do not share it.
Itâs a perfect way to create isolated service instances for different UI sections â for example, different tabs or widgets on a page.
đïž Enter ViewProviders â The Hidden Twin
Now, letâs talk about the lesser-known twin: viewProviders.
They do the same thing â provide services â but only for the componentâs view hierarchy, not for its projected content (i.e., ).
In simpler terms:
viewProviders = âmy component and its template children onlyâ
providers = âmy component, my template children, and any projected contentâ
Letâs look at an example to see this difference in action.
đ§© Example: The Real Difference
@Injectable()
export class LoggerService {
log(message: string) {
console.log(`Logger says: ${message}`);
}
}
Now, imagine we have a parent and child component:
@Component({
selector: 'child-comp',
template: `<p>Child Component Loaded</p>`
})
export class ChildComponent {
constructor(private logger: LoggerService) {
this.logger.log('Child Component using LoggerService');
}
}
@Component({
selector: 'app-parent',
template: `
<child-comp>
<p>Projected Content Here</p>
</child-comp>
`,
viewProviders: [LoggerService]
})
export class ParentComponent {
constructor(private logger: LoggerService) {
this.logger.log('Parent Component using LoggerService');
}
}
Hereâs what happens:
Both ParentComponent and ChildComponent can inject LoggerService.
But if the projected content (
Projected Content Here
) tried to inject it â it wonât work because viewProviders donât expose services to projected content.If you replace viewProviders with providers, then the projected content would also have access to LoggerService.
âïž providers vs viewProviders â Head-to-Head Comparison
đĄ When Should You Use Each?
â
Use providers When:
Youâre building a parent component that exposes a service to its children and content.
(e.g., TabsComponent sharing state with TabContent)
You want shared state between component and content projected via .
Your service handles communication or coordination between parent and projected components.
â Use viewProviders When:
You want to encapsulate internal behavior (e.g., a form fieldâs internal logic).
You donât want the projected content to accidentally inject or modify internal services.
Youâre building UI libraries where isolation and clean boundaries are critical.
đ§ Pro Tip: Why Angular Created ViewProviders
When Angular introduced viewProviders, the goal was encapsulation.
Imagine youâre building a custom InputComponent that uses an internal ControlService to manage focus and validation.
If someone projects custom content into that input (say, or ), you donât want them to accidentally inject your ControlService and modify internal behavior.
Thatâs exactly where viewProviders shine â they make sure your internal DI context stays private to your view.
đ§ Summary
Understanding the difference between providers and viewProviders can save you hours of debugging, especially in large apps or library code.
providers â Used when the service should be visible to everything â view + content.
viewProviders â Used when the service should stay internal â view only.
Think of it like:
đ providers â âShared accessâ
đ viewProviders â âPrivate accessâ
đ Final Thoughts
Angularâs dependency injection isnât just a feature â itâs a design philosophy.
By using viewProviders, you make your components more modular, encapsulated, and reusable â especially in large enterprise applications or shared UI libraries.
So next time youâre creating a new component, ask yourself:
âShould this service be visible outside my view?â
That simple question will help you choose between providers and viewProviders â and write cleaner, more maintainable Angular code. đȘ
âš Bonus Tip:
If youâre building Angular libraries or design systems, prefer viewProviders for internal logic.
It prevents service leaks and ensures that each component behaves independently, no matter where itâs used.

Top comments (0)