Linting rules specific to Ember components, enforcing modern component patterns and preventing common anti-patterns. These rules help maintain consistency and best practices in Ember component development.
const eslintPluginEmber = require('eslint-plugin-ember');
// Access component rules
const componentRules = eslintPluginEmber.rules;ES Module:
import eslintPluginEmber from 'eslint-plugin-ember';
// Access component rules
const componentRules = eslintPluginEmber.rules;Rules governing action handling and event management in components.
/**
* Disallow the actions hash in components, controllers, and routes
* Encourages use of @action decorator instead
*/
'ember/no-actions-hash': ESLintRule;
/**
* Disallow on() calls in components
* Prevents direct event binding in component lifecycle
*/
'ember/no-on-calls-in-components': ESLintRule;
/**
* Require closure actions over sendAction
* Enforces modern action passing patterns
*/
'ember/closure-actions': ESLintRule;Usage Examples:
// ❌ Bad - actions hash (triggers no-actions-hash)
export default Component.extend({
actions: {
handleClick() {
// action logic
}
}
});
// ✅ Good - @action decorator
export default class MyComponent extends Component {
@action
handleClick() {
// action logic
}
}
// ❌ Bad - sendAction (triggers closure-actions)
this.sendAction('onUpdate', value);
// ✅ Good - closure action
this.args.onUpdate(value);Rules for managing component lifecycle hooks and preventing deprecated patterns.
/**
* Disallow component lifecycle hooks that are deprecated
* Enforces modern lifecycle patterns
*/
'ember/no-component-lifecycle-hooks': ESLintRule;
/**
* Require super calls in lifecycle hooks
* Ensures proper inheritance chain execution
*/
'ember/require-super-in-lifecycle-hooks': ESLintRule;Usage Examples:
// ❌ Bad - deprecated lifecycle hook
export default Component.extend({
didInsertElement() {
// This hook is deprecated
}
});
// ✅ Good - modern patterns with modifiers
<template>
<div {{did-insert this.setup}}></div>
</template>
// ❌ Bad - missing super call
export default class extends Component {
willDestroy() {
this.cleanup();
// Missing super call
}
}
// ✅ Good - proper super call
export default class extends Component {
willDestroy() {
super.willDestroy(...arguments);
this.cleanup();
}
}Rules governing component properties and attribute handling.
/**
* Disallow this.attrs in components
* Enforces args usage over deprecated attrs
*/
'ember/no-attrs-in-components': ESLintRule;
/**
* Disallow attrs snapshot usage
* Prevents deprecated attrs.foo pattern
*/
'ember/no-attrs-snapshot': ESLintRule;
/**
* Disallow empty attrs objects
* Removes unnecessary empty attrs declarations
*/
'ember/no-empty-attrs': ESLintRule;Usage Examples:
// ❌ Bad - using this.attrs (triggers no-attrs-in-components)
export default Component.extend({
someProperty: computed('attrs.value', function() {
return this.attrs.value * 2;
})
});
// ✅ Good - using args
export default class MyComponent extends Component {
@computed('args.value')
get someProperty() {
return this.args.value * 2;
}
}
// ❌ Bad - empty attrs (triggers no-empty-attrs)
export default Component.extend({
attrs: {}
});
// ✅ Good - no attrs declaration needed
export default class MyComponent extends Component {
// No attrs needed
}Rules enforcing modern component patterns over classic Ember patterns.
/**
* Disallow classic component patterns
* Encourages native class components
*/
'ember/no-classic-components': ESLintRule;
/**
* Disallow classic classes in general
* Promotes native ES classes
*/
'ember/no-classic-classes': ESLintRule;
/**
* Disallow empty Glimmer component classes
* Removes unnecessary empty component files
*/
'ember/no-empty-glimmer-component-classes': ESLintRule;Usage Examples:
// ❌ Bad - classic component (triggers no-classic-components)
export default Component.extend({
tagName: 'div',
classNames: ['my-component']
});
// ✅ Good - modern Glimmer component
export default class MyComponent extends Component {
// Modern component patterns
}
// ❌ Bad - empty Glimmer component (triggers no-empty-glimmer-component-classes)
export default class MyComponent extends Component {
// Empty class with no purpose
}
// ✅ Good - component with actual functionality
export default class MyComponent extends Component {
@tracked isVisible = true;
@action
toggle() {
this.isVisible = !this.isVisible;
}
}Rules for organizing component properties and maintaining consistent structure.
/**
* Enforce order of properties in components
* Maintains consistent component structure
*/
'ember/order-in-components': ESLintRule;
/**
* Require tagless components
* Encourages modern component architecture
*/
'ember/require-tagless-components': ESLintRule;Usage Examples:
// ❌ Bad - incorrect property order (triggers order-in-components)
export default Component.extend({
actions: {},
someProperty: 'value',
init() {
this._super(...arguments);
},
classNames: ['my-component']
});
// ✅ Good - correct property order
export default Component.extend({
classNames: ['my-component'],
someProperty: 'value',
init() {
this._super(...arguments);
},
actions: {}
});
// ❌ Bad - component with wrapper element
export default Component.extend({
tagName: 'div'
});
// ✅ Good - tagless component
export default class MyComponent extends Component {
// No wrapper element needed
}Rules for component template handling and built-in component usage.
/**
* Disallow built-in form components
* Encourages custom form component patterns
*/
'ember/no-builtin-form-components': ESLintRule;Usage Examples:
// ❌ Bad - built-in form components (triggers no-builtin-form-components)
{{input value=this.name}}
{{textarea value=this.description}}
// ✅ Good - custom form components
<Input @value={{this.name}} @onInput={{this.updateName}} />
<Textarea @value={{this.description}} @onInput={{this.updateDescription}} />Rules for managing component state and preventing common pitfalls.
/**
* Disallow assignment of untracked properties used in tracking contexts
* Prevents reactivity issues
*/
'ember/no-assignment-of-untracked-properties-used-in-tracking-contexts': ESLintRule;
/**
* Disallow tracked properties derived from args
* Prevents unnecessary tracking overhead
*/
'ember/no-tracked-properties-from-args': ESLintRule;Rules for managing component decorators and modern class patterns.
/**
* Enforce using correct hooks for both classic and non-classic classes
* Ensures proper lifecycle hook usage in modern components
*/
'ember/classic-decorator-hooks': ESLintRule;
/**
* Disallow usage of classic APIs in classes not decorated with @classic
* Prevents mixing classic and modern patterns incorrectly
*/
'ember/classic-decorator-no-classic-methods': ESLintRule;Usage Examples:
// ❌ Bad - using classic hook in modern class (triggers classic-decorator-hooks)
export default class MyComponent extends Component {
didInsertElement() {
// Classic hook in modern class
}
}
// ✅ Good - using modern patterns
export default class MyComponent extends Component {
@action
setupElement(element) {
// Modern pattern with modifiers or actions
}
}
// ❌ Bad - classic API without @classic (triggers classic-decorator-no-classic-methods)
export default class MyComponent extends Component {
init() {
this.set('value', 0); // Classic API in modern class
}
}
// ✅ Good - modern property assignment
export default class MyComponent extends Component {
@tracked value = 0; // Modern tracked property
}Rules for component template handling and render modifier usage.
/**
* Disallow importing from @ember/render-modifiers
* Prevents usage of deprecated render modifier imports
*/
'ember/no-at-ember-render-modifiers': ESLintRule;Usage Examples:
// ❌ Bad - deprecated render modifier import (triggers no-at-ember-render-modifiers)
import { modifier } from '@ember/render-modifiers';
// ✅ Good - use ember-modifier package instead
import { modifier } from 'ember-modifier';// ❌ Bad - untracked property in tracking context export default class MyComponent extends Component { someValue = 0; // Not tracked but used in template }
// ✅ Good - properly tracked property export default class MyComponent extends Component { @tracked someValue = 0; }
// ❌ Bad - tracked property from args (triggers no-tracked-properties-from-args) export default class MyComponent extends Component { @tracked derivedValue = this.args.value; }
// ✅ Good - computed property from args export default class MyComponent extends Component { get derivedValue() { return this.args.value; } }