Skip to main content
This guide covers Angular-specific patterns and best practices enforced by ESLint rules and team conventions.

Component Patterns

Component Class Suffix

All components must use the Component suffix (@angular-eslint/component-class-suffix):

Directive Class Suffix

All directives must use the Directive suffix (@angular-eslint/directive-class-suffix):

Change Detection Strategy

Prefer OnPush change detection for better performance (@angular-eslint/prefer-on-push-component-change-detection):
Benefits:
  • Improved performance by reducing change detection cycles
  • Forces better data flow patterns
  • Explicit state management

Lifecycle Hooks

Implement Lifecycle Interfaces

Always implement the corresponding interface (@angular-eslint/use-lifecycle-interface):

Contextual Lifecycle

Use lifecycle hooks appropriately for their context (@angular-eslint/contextual-lifecycle):
  • Don’t use component hooks in directives
  • Don’t use directive hooks in components
  • Use the right hook for the right job

Input/Output Patterns

No Input/Output Metadata

Don’t use inputs and outputs metadata arrays (@angular-eslint/no-inputs-metadata-property, @angular-eslint/no-outputs-metadata-property):

No Output Rename

Don’t rename outputs (@angular-eslint/no-output-rename):

Prefer Signals

Use Angular signals for reactive state (@angular-eslint/prefer-signals):

EventEmitter Type

Use EventEmitter with proper typing (@angular-eslint/prefer-output-emitter-ref):

Dependency Injection

Constructor Injection

Use constructor injection for services:

Modern inject() Function

While @angular-eslint/prefer-inject is currently disabled, the inject() function is available:

RxJS Best Practices

Automatic Unsubscription

Use takeUntilDestroyed to automatically unsubscribe (rxjs-angular/prefer-takeuntil):

Subject Protection

Don’t expose raw Subject instances - only expose as Observable (rxjs/no-exposed-subjects):
All rules from eslint-plugin-rxjs recommended config are enabled, including:
  • No nested subscriptions
  • No subscribe in subscribe
  • Proper error handling
  • Unsubscribe from finite observables

Template Patterns

Button Type Attribute

All buttons must have an explicit type attribute (@angular-eslint/template/button-has-type):

Inline Templates

Inline templates are processed automatically by ESLint (angular.processInlineTemplates):

Custom Bitwarden Rules

Required Labels on Icons

Icon buttons require accessibility labels (@bitwarden/components/require-label-on-biticonbutton):

No BWI Class Usage

Avoid using bwi-* classes directly in templates (@bitwarden/components/no-bwi-class-usage):

No Icon Children in Buttons

Don’t nest icons inside bit-button components (@bitwarden/components/no-icon-children-in-bit-button):

Pipes

Implement PipeTransform

All pipes must implement the PipeTransform interface (currently disabled):

Standalone Components

While @angular-eslint/prefer-standalone is currently disabled, standalone components are the modern approach:

Admin Console Strict Rules

The libs/admin-console and admin-console app directories enforce stricter Angular rules:
  • @angular-eslint/no-empty-lifecycle-method: "error"
  • @angular-eslint/no-input-rename: "error"
  • @angular-eslint/no-output-native: "error"
  • @angular-eslint/no-output-on-prefix: "error"
  • @angular-eslint/use-pipe-transform-interface: "error"
See eslint.config.mjs:609-631 for the full configuration.

Next Steps