With the rise of standalone components in Angular, most developers have become familiar with the various provider functions available. Commonly encountered examples include provideHttpClient() and provideRouter(). Internally, these functions register providers, enabling the associated features. Based on their names, one might assume these belong in a component’s providers: [] array. In reality, they are intended for use in environment injectors.
Why this matters
Suppose you are building a custom logger library. At some point, you will likely create a provideLogger() function that registers a configuration provider.
export function provideLogger(config: Partial<LoggerConfig>) {
return {
provide: LoggerConfig,
useValue: config,
};
}
This function might be used within a component or directive injector without triggering any immediate errors. Over time, however, this can introduce bugs or lead to unpredictable behavior in your library. If provideLogger is designed to establish global logging settings, using it in different components with varying parameters can result in inconsistent logger behavior — even though such usage was never intended.

How to fix it
Angular offers the makeEnvironmentProviders function, which returns an EnvironmentProviders type rather than a standard provider array. This type wraps the providers, ensuring that your function is only invoked in appropriate contexts, such as within bootstrapApplication or an ApplicationConfig.
To implement this, update provideLogger to leverage makeEnvironmentProviders:
import { makeEnvironmentProviders } from "@angular/core";
export function provideLogger(config: Partial<LoggerConfig>) {
return makeEnvironmentProviders([
{
provide: LoggerConfig,
useValue: config,
},
]);
}
Once this change is made, attempting to call provideLogger() inside a component or directive will produce an error. This error promptly indicates that the providers are intended for environment-level configuration and cannot be used there.



