eslint-plugin-angular-modern
August 21, 2026 · View on GitHub
80+ ESLint rules for modern and safe Angular.
Note
Find this tool useful? I’m open to freelance & full-time opportunities. Feel free to reach out on LinkedIn or Bluesky.
Goals
- Enforce modern Angular
Angular has evolved a lot: standalone components, new control flow syntax, new dependency injection, zoneless, signals... One could even argue it is a new framework. It has been done in a backward compatible way, which is nice, but it also has a downside: a lot of legacy features are still available, both for developers and for AI tools. Lint rules can restrict the usage of these legacy features, in a reliable way (unlike AI custom instructions and skills, which are often not followed).
A blog post explains this goal in more details.
- Enforce safe Angular usage
Some of the new features, especially the new dependency injection, can produce runtime errors, not protected by compilation. Lint rules can lower the risk of these runtime errors.
Another blog post explains this goal in more details.
Requirements
- TypeScript ESLint v8
- New flat ESLint configuration (
eslint.config.jsor equivalent)
Note
.eslintrc.json and other legacy ESLint configurations are not supported.
Important
The default presets target the latest Angular stable version. Some rules may be unsuitable for previous versions, see "Angular versions" section below to adapt the configuration accordingly.
Getting started
- Installation
npm install eslint-plugin-angular-modern --save-dev
- ESLint flat configuration (
eslint.config.jsor equivalent)
const eslint = require('@eslint/js');
const { defineConfig } = require('eslint/config');
const tsEslint = require('typescript-eslint');
const angularModern = require('eslint-plugin-angular-modern'); // ⬅️ add this
module.exports = defineConfig({
files: ['**/*.ts'],
languageOptions: {
parserOptions: {
projectService: true,
},
},
extends: [
eslint.configs.recommended,
tsEslint.configs.strictTypeChecked,
tsEslint.configs.stylisticTypeChecked,
angularModern.configs.recommended, // ⬅️ add this (or some of the other presets below)
],
rules: {},
});
npm run lint
Note
In VS Code, it may be required to restart for the ESLint extension to apply the new rules.
Migration
If coming from angular-eslint-injection-context or angular-eslint-zoneless libraries, a migration guide is available.
Recommended preset
{ extends: [angularModern.configs.recommended] }
The recommended preset enables all the rules of all the features presets below. It is the recommended preset for:
- new projects
- projects already fully migrated to modern Angular
Features presets
A feature preset enables the rules for a specific set of features. It is recommended for projects only partially migrated to modern Angular. The presets available are:
- injection context: safety rules for
inject()and else - dependency injection: no constructor injection
- zoneless: no zone.js features
- signals: no legacy reactivity
- standalone: no NgModules
- functional: no class-based APIs
- styling bindings: no legacy styling bindings
- host bindings: no legacy host bindings
- fetch HTTP: no XHR HTTP
Injection context preset
{ extends: [angularModern.configs.injectionContext] }
These safety rules check that inject() and similar functions (toSignal(), resource(), form()...) are called in an injection context, to avoid the NG0203 runtime error:
- inject-in-injection-context
- inject-async-in-injection-context
- effect-in-injection-context
- after-every-render-in-injection-context
- after-next-render-in-injection-context
- after-render-effect-in-injection-context
- signal-form-in-injection-context
- resource-in-injection-context
- rx-resource-in-injection-context
- take-until-destroyed-in-injection-context
- to-observable-in-injection-context
- to-signal-in-injection-context
- pending-until-event-in-injection-context
Tip
This safety preset can and should always be enabled, even if a project has not yet migrated to inject().
Custom functions
For other functions requiring the injection context (custom ones or from libraries), an additional rule is available:
Dependency injection preset
{ extends: [angularModern.configs.dependencyInjection] }
These rules enforce using the new dependency injection system:
Zoneless preset
{ extends: [angularModern.configs.zoneless] }
These rules enforce a zoneless application to not use zone.js-based features:
- no-zonejs-import
- no-providezonechangedetection
- no-ngzone
- no-ngzone-testing
- no-zonejs-testing-functions
Signals preset
{ extends: [angularModern.configs.signals] }
These rules enforce a project to handle reactivity with signals:
- no-directive-writable-property
- no-directive-accessor
- no-eager-change-detection
- no-ngoninit
- no-ngdocheck
- no-ngonchanges
- no-ngcontentviewinit
- no-ngcontentviewchecked
- no-ngafterviewinit
- no-ngafterviewchecked
- no-ngondestroy
- no-input-decorator
- no-output-decorator
- no-content-decorator
- no-view-decorator
- no-attribute-decorator
- no-asyncpipe
- no-reactive-forms
- no-detectchanges-testing
- no-changedetectorref
Tip
This preset should only be enabled after a project has fully migrated to signals. Otherwise, individual rules should be enabled gradually.
Standalone preset
{ extends: [angularModern.configs.standalone] }
These rules enforce a standalone application to not use NgModules:
- no-ngmodule
- no-createngmodule
- no-platformbrowser
- no-browsermodule
- no-browsertestingmodule
- no-applicationmodule
- no-commonmodule
- no-routermodule
- no-serviceworkermodule
- no-servermodule
- no-withmodule-testing
Tip
To fully enforce standalone, 2 other checks must be enabled too:
- the
strictStandaloneAngular compiler option: documentation - the
@typescript-eslint/no-deprecatedrule: documentation
Libraries modules
Additional individual rules, not included in the preset, are available for some libraries:
Functional preset
{ extends: [angularModern.configs.functional] }
These rules enforce functional APIs instead of class-based APIs:
- no-canactivate-class
- no-canactivatechild-class
- no-candeactivate-class
- no-canmatch-class
- no-resolve-class
- no-httpinterceptor-class
- no-httpinterceptors-token
- no-withinterceptorsfromdi
Styling bindings preset
{ extends: [angularModern.configs.stylingBindings] }
These rules enforce native styling bindings:
Host bindings preset
{ extends: [angularModern.configs.hostBindings] }
These rules enforce typed checked host bindings:
Fetch HTTP preset
{ extends: [angularModern.configs.fetchHttp] }
These rules enforce to not use XHR HTTP:
Other rules
The following invidiual rules are not enabled in presets.
Rules already reported by strictStandalone
See the documentation to enable Angular strictStandalone compiler option.
Rules already reported by @typescript-eslint/no-deprecated
See the documentation to enable @typescript-eslint/no-deprecated rule.
- no-platformbrowserdynamic
- no-routertestingmodule
- no-httpclientmodule
- no-httpclientjsonpmodule
- no-httpclientxsrfmodule
- no-httpclienttestingmodule
- no-browseranimationsmodule
- no-noopanimationsmodule
- no-canload-class
Experimental
Rules still being tested.
Angular versions
The presets above target the latest Angular stable version (currently v22). Some rules may be unsuitable for previous versions and must be disabled. For Angular 20 & 21:
module.exports = defineConfig({
rules: {
'eslint-plugin-angular-modern/no-injectable-decorator': 'off',
'eslint-plugin-angular-modern/no-reactive-forms': 'off',
'eslint-plugin-angular-modern/no-http-reportprogress': 'off',
},
});
FAQ
Why not in Angular ESLint?
I proposed a Pull Request, but it has been ignored for months, then rejected without a reason. So I decided to publish the rule by myself, and to add many more.
Is Angular ESLint required?
No, these rules only depends on TypeScript ESLint. Both can be used in a project, but some rules may be redundant. See the documentation for optimization.
Is adding a plugin making the project heavier?
No, the package has 0 dependency. It just add lint rules using TypeScript ESLint, which is already installed in the project. And the main design choice is performance.
Why is X or Y feature considered as legacy?
This is explained in the "Legacy" section of the design choices.
License
MIT