Service Levels in Angular

Published: (December 20, 2025 at 02:05 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Root-Level Services: The Global MVP 🌍

When you generate a service with the Angular CLI (ng generate service), the default looks like this:

@Injectable({
  providedIn: 'root'
})
export class UserService { }

Angular creates one single instance of this service and shares it across the entire application.
Think of it as a front‑desk clerk in a hotel: every component talks to the same clerk and receives the same information.

Typical use cases

  • Authentication Service – share login state across all components
  • Configuration Service – global app settings that don’t change
  • HTTP/API Service – a single point for backend communication
  • State Management – shared data needed by multiple components

Root‑level services are the go‑to choice for most scenarios because they are simple, efficient, and avoid unnecessary instances.

Component-Level Services: Keep It Local 🏠

If a service should be used only by a single component—or each instance of a component needs its own copy—you can provide it at the component level:

@Component({
  selector: 'app-shopping-cart',
  templateUrl: './shopping-cart.component.html',
  providers: [CartService]
})
export class ShoppingCartComponent { }

Each time the component is created, Angular creates a new instance of CartService for that component only.
Analogy: a private caretaker for each hotel room; Room 101 has its own caretaker, Room 102 has another, and they don’t share information.

When to use

  • Form State – each form component keeps its own data
  • Component‑Specific Logic – data that must not leak to other components
  • Multiple Instances – the same component appears multiple times on a page and needs independent behavior

Example: a ProductFilterComponent displayed on several pages, each remembering its own filters.

Module-Level Services: The Middle Ground ⚖️

If you’re using Angular modules (instead of standalone components), you can provide services at the module level:

@NgModule({
  declarations: [...],
  imports: [...],
  providers: [AuthGuardService]
})
export class AdminModule { }

Eagerly Loaded Module

When the module loads at application start, the service behaves like a singleton across the whole app—effectively the same as providedIn: 'root'.

Lazy‑Loaded Module

When a module is lazy‑loaded (e.g., via a route), Angular creates a separate injector for that module, giving the service a new instance scoped to the lazy‑loaded feature.

const routes: Routes = [
  { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }
];

In this case, AdminService exists only within the lazy‑loaded admin module and is destroyed when the module is unloaded.

Which One Should You Use? 🤔

QuestionRecommended Scope
Is the service needed everywhere?Root‑Level ✅
Is the service used by only one component?Component‑Level ✅
Is the service scoped to a feature module that lazy‑loads?Module‑Level ✅

Understanding Angular’s Dependency Injection hierarchy—how injectors resolve dependencies up the component tree—helps you decide the appropriate level.

The Bottom Line 💡

  • Root‑level: One instance, shared everywhere.
  • Component‑level: New instance per component.
  • Module‑level: Instance depends on eager vs. lazy loading.

Choosing the right level keeps your app maintainable and performant; the wrong choice can make debugging a headache.

Back to Blog

Related posts

Read more »

Angular pipes: Time to rethink

Forem Communities Forem !Forem Logohttps://media2.dev.to/dynamic/image/width=65,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3...