Enforcing Self-Closing Component Tags with an Angular ESLint Rule
The Angular team introduced support for self-closing tags in v15.1. This small addition has been a nice quality-of-life improvement for developers working with components. It lets you use the self-closing syntax for components that have no content, or when the content is optional.
For those who need a quick reminder, the self-closing tag syntax appears like this:
<!-- Normal syntax -->
<my-component></my-component>
<!-- Self-closing tag syntax -->
<my-component />
Once we moved to Angular v15.1 at our workplace, we began adopting this syntax in some places but not everywhere. There was no automated way to keep things consistent, so it ultimately came down to individual developer memory.
That changed when I found out that angular-eslint released a new lint rule in v16.2. The rule, named prefer-self-closing-tags, can be configured to enforce this style either partially or across an entire codebase.
Because angular-eslint was already a dependency in our package.json, setting things up was straightforward — just a matter of adding the rule to an ESLint configuration file.
A bit of context on our setup: we manage a monorepo with Nx, which contains two primary applications and over a hundred libraries. Each of these libraries has its own ESLint config that inherits from a root configuration. We decided to place the lint rule in that root configuration so it applies everywhere.
Here is what the configuration looked like:
{
...
"files": ["*.component.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {
"@angular-eslint/template/prefer-self-closing-tags": ["error"]
}
...
},
The *.component.html pattern in the files array was chosen deliberately. We wanted the rule to apply only to Angular component templates, not to standard HTML files.
Every Angular project contains a root index.html, which is a regular HTML file where the top-level component is mounted, for instance <my-app></my-app>. Using <my-app /> there would be invalid HTML by specification, because custom elements — which Angular components ultimately are — cannot be self-closing. So that file pattern is designed to skip the main index.html and other non-component HTML files.
But wait, how does the browser or the compiler handle self-closing tags inside Angular components? Wouldn't they also be invalid? The Angular compiler resolves this by converting them into the standard syntax during the build process. For instance, <my-component /> is transformed into <my-component></my-component> after the build.
Another thing you might have noticed is that the pattern only mentions HTML files. What about inline templates? No need to worry. Even though the rule is scoped to HTML files, the angular-eslint tooling also inspects inline templates for potential self-closing tag candidates.
A notable feature of this lint rule is that it comes with an autofix option. After adding the configuration, you can simply run your lint command with the --fix flag. The angular-eslint will then scan for every location where a self-closing tag is possible and automatically update your code. You can then review the changes before committing them. Pretty convenient.
If you have Prettier set up, be sure to use v2.8.2 or a newer version. That way Prettier will also properly format self-closing components.
Using self-closing tags in Angular templates reduces the amount of code you need to write, which improves the overall developer experience. Let the framework handle the heavy lifting.
Now you have a clear path to standardize self-closing tags across your Angular project with ESLint. Don't hesitate to adopt this practice.
Thanks for reading, and see you in the next post 🙂
Special thanks to Lars Gyrup Brink Nielsen for reviewing this article.
Cover photo by Agence Olloweb on Unsplash.
