ESLint for Angular 📐
ESLint is the standard linting tool for Angular projects. It catches bugs, enforces coding standards, and helps maintain consistency across your codebase. Angular has an official ESLint integration — angular-eslint — that provides Angular-specific rules for both TypeScript and HTML templates.
🎯 Setup with angular-eslint
Section titled “🎯 Setup with angular-eslint”The easiest way to add ESLint to an Angular project is with the ng add schematic:
ng add @angular-eslint/schematicsThis command:
- Installs
eslint,@angular-eslint/*, andtypescript-eslintpackages - Creates an
eslint.config.jsfile with Angular-recommended rules - Adds a
linttarget toangular.jsonso you can runng lint
⚙️ Configuration (Flat Config)
Section titled “⚙️ Configuration (Flat Config)”After setup, your eslint.config.js looks like this:
// @ts-checkconst eslint = require('@eslint/js');const tseslint = require('typescript-eslint');const angular = require('angular-eslint');
module.exports = tseslint.config( { files: ['**/*.ts'], extends: [ eslint.configs.recommended, ...tseslint.configs.recommended, ...tseslint.configs.stylistic, ...angular.configs.tsRecommended, ], processor: angular.processInlineTemplates, rules: { '@angular-eslint/directive-selector': [ 'error', { type: 'attribute', prefix: 'app', style: 'camelCase', }, ], '@angular-eslint/component-selector': [ 'error', { type: 'element', prefix: 'app', style: 'kebab-case', }, ], }, }, { files: ['**/*.html'], extends: [ ...angular.configs.templateRecommended, ...angular.configs.templateAccessibility, ], rules: {}, },);Configuration Breakdown
Section titled “Configuration Breakdown”files: ['**/*.ts']— Apply TypeScript rules to all.tsfilesprocessInlineTemplates— Lint inline templates in@Component({ template })stringsfiles: ['**/*.html']— Apply template rules to all.htmlfilestemplateAccessibility— Enables accessibility-focused rules for templates
📏 Recommended Rules for Angular
Section titled “📏 Recommended Rules for Angular”TypeScript Rules
Section titled “TypeScript Rules”Add these rules to the TypeScript config section for stricter Angular code:
rules: { // Enforce consistent component architecture '@angular-eslint/prefer-on-push-component-change-detection': 'warn', '@angular-eslint/no-empty-lifecycle-method': 'error', '@angular-eslint/use-lifecycle-interface': 'error', '@angular-eslint/relative-url-prefix': 'error', '@angular-eslint/sort-lifecycle-methods': 'warn',
// TypeScript best practices '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', }], '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/prefer-readonly': 'warn',
// General best practices 'no-console': ['warn', { allow: ['warn', 'error'] }], 'prefer-const': 'error', 'no-var': 'error',}Template Rules
Section titled “Template Rules”Add these to the HTML config section:
rules: { // Accessibility '@angular-eslint/template/alt-text': 'error', '@angular-eslint/template/elements-content': 'error', '@angular-eslint/template/label-has-associated-control': 'error', '@angular-eslint/template/click-events-have-key-events': 'warn', '@angular-eslint/template/no-autofocus': 'warn',
// Best practices '@angular-eslint/template/no-negated-async': 'error', '@angular-eslint/template/no-duplicate-attributes': 'error', '@angular-eslint/template/use-track-by-function': 'warn', '@angular-eslint/template/prefer-self-closing-tags': 'warn',}🛡️ Custom Rules for Enforcing Patterns
Section titled “🛡️ Custom Rules for Enforcing Patterns”You can create custom rules to enforce your team’s Angular conventions.
Enforce OnPush Change Detection
Section titled “Enforce OnPush Change Detection”'@angular-eslint/prefer-on-push-component-change-detection': 'error',This reports an error on any component that doesn’t explicitly set ChangeDetectionStrategy.OnPush.
Enforce inject() Over Constructor Injection
Section titled “Enforce inject() Over Constructor Injection”Use the @angular-eslint/prefer-standalone rule and the TypeScript ESLint ecosystem to enforce modern patterns:
rules: { // Ban constructor parameter injection in favor of inject() '@angular-eslint/prefer-standalone': 'error',}Restrict Specific Imports
Section titled “Restrict Specific Imports”Prevent importing from deprecated or internal paths:
'no-restricted-imports': ['error', { patterns: [ { group: ['@angular/common/http/testing'], message: 'Use provideHttpClientTesting() from @angular/common/http instead.', }, ],}],▶️ Running the Linter
Section titled “▶️ Running the Linter”Basic Usage
Section titled “Basic Usage”ng lintThis lints all files in the project and reports errors:
/src/app/user/user.component.ts 12:3 error Component should use ChangeDetectionStrategy.OnPush @angular-eslint/prefer-on-push-component-change-detection 25:7 warning Unexpected console statement no-console
✖ 2 problems (1 error, 1 warning)Lint Specific Files
Section titled “Lint Specific Files”ng lint --lint-file-patterns="src/app/features/**/*.ts"Fix Issues Automatically
Section titled “Fix Issues Automatically”Many ESLint rules support auto-fixing:
ng lint --fixThis automatically fixes issues like:
- Sorting imports
- Adding missing semicolons
- Converting
vartoconst/let - Removing unused imports
- Adding self-closing tags in templates
🔄 Integration with CI/CD
Section titled “🔄 Integration with CI/CD”Add linting to your CI pipeline to catch issues before merging:
GitHub Actions Example
Section titled “GitHub Actions Example”name: Linton: [pull_request]
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: 'npm' - run: npm ci - run: npx ng lintPre-Commit Hook
Section titled “Pre-Commit Hook”Lint only staged files before each commit using lint-staged:
{ "lint-staged": { "*.{ts,html}": "eslint --fix" }}🧩 Common Rules to Enable/Customize
Section titled “🧩 Common Rules to Enable/Customize”Here’s a quick reference of commonly adjusted rules:
| Rule | Default | Recommended |
|---|---|---|
@angular-eslint/prefer-on-push-component-change-detection | off | warn or error |
@angular-eslint/template/prefer-self-closing-tags | off | warn |
@angular-eslint/use-lifecycle-interface | off | error |
@typescript-eslint/no-explicit-any | warn | error for strict teams |
@typescript-eslint/no-unused-vars | off | error with argsIgnorePattern |
no-console | off | warn (allow warn/error) |
@angular-eslint/template/alt-text | off | error |
@angular-eslint/template/click-events-have-key-events | off | warn |
🚫 Disabling Rules When Needed
Section titled “🚫 Disabling Rules When Needed”Sometimes you need to suppress a rule for a specific case:
// eslint-disable-next-line @typescript-eslint/no-explicit-anyconst legacyData: any = getLegacyData();For an entire file:
/* eslint-disable no-console */// This file intentionally uses console for logging