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

  1. 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.

  1. 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.js or 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

  1. Installation
npm install eslint-plugin-angular-modern --save-dev
  1. ESLint flat configuration (eslint.config.js or 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: {},
});
  1. 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.

{ 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:

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:

Signals preset

{ extends: [angularModern.configs.signals] }

These rules enforce a project to handle reactivity with signals:

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:

Tip

To fully enforce standalone, 2 other checks must be enabled too:

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:

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.

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