Quick answer: Angular dependency injection is how a component gets the services it needs: you ask with inject() and Angular supplies the registered instance. A service is a class that holds logic you want to share — fetching data, caching, formatting, state. A service provided with providedIn: 'root' is a single shared instance for the whole application.

Create a service for Angular dependency injection

ng generate service cart

The CLI creates the file and adds the @Injectable decorator. That decorator is the signal that the class can be injected, and it is where you declare the service’s scope.

import { Injectable, signal, computed } from '@angular/core';

export interface CartItem {
  id: number;
  name: string;
  price: number;
}

@Injectable({ providedIn: 'root' })
export class CartService {
  private items = signal<CartItem[]>([]);

  readonly count = computed(() => this.items().length);
  readonly total = computed(() =>
    this.items().reduce((sum, i) => sum + i.price, 0));

  add(item: CartItem) {
    this.items.update((list) => [...list, item]);
  }
}

Notice the service holds its data in signals. Any component that reads count() or total() re-renders automatically when the cart changes. This is the simplest correct way to share state in modern Angular, and it needs no state library at all.

Inject it with Angular dependency injection

import { Component, inject } from '@angular/core';
import { CartService } from './cart.service';

@Component({
  selector: 'app-cart-badge',
  template: `<span>{{ cart.count() }} items</span>`,
})
export class CartBadgeComponent {
  cart = inject(CartService);
}

inject() must be called in the class body, not inside a method, because Angular resolves it during creation. Angular’s style guide now prefers this over constructor parameters: it is terser, it inherits the correct type automatically, and it works in field initializers.

Where a service is provided decides its scope

Provided inLifetimeUse for
@Injectable({ providedIn: 'root' })One instance for the appShared data, API access, auth
Route providersOne instance per routeFeature-scoped state
Component providersOne instance per componentA fresh instance for each instance of the component
@Component({
  selector: 'app-editor',
  providers: [DraftService],     // new instance per editor
  template: `...`,
})
export class EditorComponent {
  private draft = inject(DraftService);
}

Providing at the component level is the fix when two copies of a component must not share state — for example, two independent forms on one page.

Overriding an Angular dependency injection provider for tests

Because components ask for a token rather than a concrete class, you can swap the implementation without touching the component. Define an injection token, provide a real implementation in app.config.ts, and substitute a fake one in a test.

import { InjectionToken } from '@angular/core';

export const API_URL = new InjectionToken<string>('API_URL');

// app.config.ts
providers: [{ provide: API_URL, useValue: 'https://api.example.com' }]

// usage
private url = inject(API_URL);

Injection tokens are how Angular itself exposes configuration, and they are the clean way to keep environment-specific values out of your services.

Continue with routing and navigation, or see how services carry shared state in signals and state management. To learn dependency injection with real projects, Ampersand Academy teaches Angular one-to-one.

Frequently asked questions

What does providedIn root mean?

It registers the service with the root injector, so Angular creates one shared instance for the whole application that every component receives.

Should I use inject() or constructor injection?

Angular’s style guide prefers inject(). It is shorter, gives correct types automatically, and can be used in field initializers. Constructor injection still works and is not removed.

How do I share state between components?

Put the state in a service provided in root, store it in a signal, and inject the service into every component that needs it. All consumers update automatically.

When should a service be provided at the component level?

When each instance of the component needs its own copy of the state, such as two independent forms or editors on the same page.

What is an injection token in Angular?

A token names a dependency that is not a class, such as a configuration string. You inject it with inject(TOKEN) and provide its value in the providers list.

Last updated on · Written by