Skip to content

Angular inject() Function

The inject() function is the modern way to request dependencies in Angular, usable anywhere a valid injection context exists, not just constructors. This lesson covers how it works and where it shines.

What Is inject()?

inject() is a function you call to retrieve a dependency from Angular's current injector, functionally equivalent to constructor injection but usable in more places: class field initializers, functional route guards, functional interceptors, and factory provider functions.

Because inject() is called as a plain function rather than declared as a constructor parameter, it also composes more naturally with newer APIs like input() and output(), which are also just plain functions used as class field initializers.

import { Component, inject } from '@angular/core';
import { UserService } from './user.service';

@Component({ selector: 'app-profile', standalone: true, template: '{{ name }}' })
export class ProfileComponent {
  private userService = inject(UserService);
  name = this.userService.getCurrentUserName();
}

inject(UserService) retrieves the same singleton instance you would get from constructor injection, but as a plain function call.

inject() Usage Patterns

private service = inject(MyService);
private optional = inject(MyService, { optional: true });
private token = inject(MY_TOKEN);

export const authGuard: CanActivateFn = () => {
  const auth = inject(AuthService);
  return auth.isLoggedIn();
};
  • inject(Token) throws if no provider is found, unless you pass { optional: true }.
  • inject() works with classes, InjectionTokens, and other built-in tokens like Router or ActivatedRoute.
  • Functional guards, resolvers, and interceptors rely on inject() since they have no constructor at all.
  • inject() must be called within a valid injection context, generally during class construction or inside specific Angular-provided callback functions.

inject() Cheatsheet

Where inject() is commonly used across the framework.

Context Example
Component field private http = inject(HttpClient);
Service field private router = inject(Router);
Optional dependency inject(LoggerService, { optional: true })
Functional route guard inject(AuthService) inside CanActivateFn
Functional interceptor inject(AuthService) inside HttpInterceptorFn
Factory provider useFactory: () => inject(Config).apiUrl

inject() vs Constructor Injection

Both approaches ask Angular's injector for the same dependency and produce identical results at runtime; the difference is purely syntactic and about where they can be used.

// constructor injection
export class ProfileComponent {
  constructor(private userService: UserService) {}
}

// inject() function
export class ProfileComponent {
  private userService = inject(UserService);
}

Both give this.userService the exact same singleton UserService instance.

inject() in Functional Route Guards

Modern Angular favors functional guards (plain functions) over class-based guards. Since a function has no constructor, inject() is the only way to access dependencies inside one.

export const authGuard: CanActivateFn = (route, state) => {
  const auth = inject(AuthService);
  const router = inject(Router);

  if (auth.isLoggedIn()) return true;
  router.navigate(['/login']);
  return false;
};

Simplifying Base Classes With inject()

inject() also simplifies inheritance: a base class can inject its own dependencies without forcing every subclass to repeat and forward constructor parameters.

export class BaseFormComponent {
  protected fb = inject(FormBuilder);
}

export class SignupFormComponent extends BaseFormComponent {
  // no need to redeclare or forward fb in a constructor
  form = this.fb.group({ email: [''] });
}

Common Mistakes

  • Calling inject() outside of a valid injection context, like inside a setTimeout callback or after component construction.
  • Mixing inject() and constructor injection for the same dependency in the same class inconsistently.
  • Forgetting { optional: true } for genuinely optional dependencies, causing avoidable runtime errors.
  • Assuming inject() behaves differently from constructor injection; they resolve dependencies identically.

Key Takeaways

  • inject() retrieves a dependency from Angular's injector as a plain function call.
  • It behaves identically to constructor injection but works in more places: fields, functional guards, interceptors, and factories.
  • It must be called within a valid injection context, most commonly during class field initialization.
  • It simplifies base classes and functional APIs that have no constructor to inject through.

Pro Tip

Favor inject() over constructor injection in new standalone components and services; it keeps dependencies declared as regular class fields, which reads more consistently alongside input(), output(), and signal-based state.