How to Setup ESLint and Prettier in an Angular Project
Tags: eslinttypescript, prettier, angular, eslint
Published: 2023-06-03 14:08:24

---
title: How to Setup ESLint and Prettier in an Angular Project
published: true
date: 2023-06-02 14:15:54 UTC
tags: eslinttypescript,prettier,angular,eslint
canonical_url: https://medium.com/@seanhaddock_60973/how-to-setup-eslint-and-prettier-in-an-angular-project-82065799bc00
---

![vacuuming confetti](https://cdn-images-1.medium.com/max/1024/0*zQiaz_trm8olEJAr)
_Photo by No Revisions on Unsplash_

_ESLint_ is the recommended code _linting_ tool for Angular applications to help ensure good code quality and prevent bugs before they happen. For example, the no-const-assign rule prevents you from modifying a const, which will cause a runtime error:

![showing a cannot assign to constant error](https://cdn-images-1.medium.com/max/752/1*-RoafFwmTAQZdg0KyzTdiQ.png)
_Example of a no-const-assign lint error_

_Prettier_ is a popular code _formatting_ tool which formats code in a way that promotes readability and helps development shops achieve consistency between code bases. For example, Prettier changes this:

```
 <div><p><span>Hello World</span>
    </p></div>
```

to this:

```
<div>
  <p><span>Hello World</span></p>
</div>
```

A trivial example, but multiply instances like these by a thousand and you can imagine the impact Prettier can have on the readability and consistency of a code base.

While ESLint has some formatting rules as well, many people prefer Prettier due to its more opinionated nature, which leaves fewer things to be configured — i.e., fewer decisions to be made. Prettier also formats files other than JavaScript/TypeScript out of the box. Overall, Prettier is a more comprehensive code formatting tool.

> Taken together, ESLint and Prettier help ensure consistent, high-quality code bases that follow best practices.

In this post, I want to talk about how to configure ESLint and Prettier for an Angular application. I’ll also briefly explore how to publish shared configuration so that all of your applications can follow the same linting and formatting standards.

Code linting and formatting are in many ways about reducing the number of options available when writing code, but ironically, there are a _lot_ of options when it comes to configuring these tools. Rather than list all of the options available when detailing how to set these up, I’m going to provide an opinionated setup that I think is common.

### Adding ESLint

The first step in adding ESLint to an Angular application is to run this command:

```
ng add @angular-eslint/schematics
```

You can optionally pass a projectName parameter, but if you have a single project in the repo, ESLint will be configured automatically, which entails:

Adding dependencies:

- [**eslint**](https://github.com/eslint/eslint): ESLint is a _JavaScript_ linting library and is not Angular or TypeScript specific
- [**@typescript-eslint/parser**](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser): Since ESLint is a _JavaScript_ linting tool, this package is needed to allow for linting _TypeScript_ code
- [**@typescript-eslint/eslint-plugin**](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin): Provides TypeScript specific rules
- [**@angular/eslint/builder**](https://github.com/angular-eslint/angular-eslint/blob/main/packages/builder): Allows execution of ESLint — e.g., ng lint
- [**@angular-eslint/eslint-plugin**](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin): Provides Angular specific rules
- [**@angular-eslint/eslint-plugin-template**](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template) and [**@angular-eslint/template-parser**](https://github.com/angular-eslint/angular-eslint/blob/main/packages/template-parser): Provide rules for Angular _templates_
- [**@angular-eslint/schematics**](https://github.com/angular-eslint/angular-eslint/blob/main/packages/schematics): Used to generate configuration files, as it did with the above command

Adding/Updating config files:

- Adds a lint npm task to **package.json** which lints the project
- Adds the lint target to **angular.json** as well as a schematics entry under cli so that future apps and libs in the workspace will be automatically configured to use ESLint.
- Adds an  **.eslintrc.json** file that extends from eslint, typescript-eslint and angular-eslint which applies the linting rules those packages provide. It also adds rules to make sure directive and component selectors are prefixed with app which can be changed if necessary. **This file is where the actual linting rules come from.**

With this step complete, you can now run ng lint or npm run lint and enforce the recommended linting rules from eslint, typescript-eslint and angular-eslint.

### Adding Prettier

Prettier recommends installing prettier locally in your project with the _exact _version:

```
npm install prettier -D --save-exact
```

Next add a  **.prettierrc.json** file to house the Prettier options (what few there are) and a . **prettierignore** file to specify which files/directories should be ignored (note that node\_modules is ignored by default):

![example .prettierrc.json and .prettierignore](https://cdn-images-1.medium.com/max/828/1*HCD-2ZHkSylLWqLHs-l6gg.png)
_example .prettierrc.json and .prettierignore_

#### Adding Prettier ESLint Config

Some ESLint rules conflict with Prettier ones, so Prettier has published an eslint-config that disables those rules. Run the following:

```
npm install -D eslint-config-prettier
```

Now you just have to extend from prettier in  **.eslintrc.json** (make sure it’s the **_last _** entry):

```
"extends": [
  ...
  "prettier"
],
```

### Editor Configuration

ESLint and Prettier can be run from the command line, but you’ll want to integrate them into your editor of choice.

For VS Code, you can install the [_ESLint_](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)and [_Prettier — Code formatter_](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) extensions.

Press Ctrl + ,and then click the _Open Settings (JSON)_ icon in the top right:

![](https://cdn-images-1.medium.com/max/1022/1*QGbeCpa0CrY5UZkD4kTmGw.png)

Then add these entries to **settings.json** :

```
"editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
```

These settings auto-fix ESLint errors on _Save_ (using the ESLint extension), set Prettier as the default formatter for all file types, and auto-format on _Save_ (using the Prettier extension).

### Commit Hook

It’s possible to use [_Husky_](https://typicode.github.io/husky/) and [_lint-staged_](https://github.com/okonet/lint-staged) to automatically fix linting and formatting errors pre-commit. However, I elect not to do that because:

1. As a matter of principle, I’m not sure how I feel about altering code after the developer has “committed” to “committing” — even if it should have no effect on functionality
2. It seems a bit slow to me

To me, at least for now, the better option seems to be to have everyone install the ESLint and Prettier extensions and auto-fix|format on _Save_. You’ll want to run ESLint and Prettier in your CI to block PRs that have lint or format errors, thus encouraging everyone to use the extensions:

```
ng lint
npx prettier --check **/* --ignore-unknown
```

### Rules

#### ESLint

We are now extending the following rule sets for TypeScript files in the  **.eslintrc.json** file. Note that order DOES matter, and if there are conflicting rules, the last one wins.

```
"files": ["*.ts"],
"extends": [
  "eslint:recommended",
  "plugin:@typescript-eslint/recommended",
  "plugin:@angular-eslint/recommended",
  "plugin:@angular-eslint/template/process-inline-templates",
  "prettier"
]
```

- [eslint:recommended](https://eslint.org/docs/latest/rules/) (the ones with a checkmark in the provided link are in the “recommended” set applied here)
- [plugin:@typescript-eslint/recommended](https://typescript-eslint.io/rules/) (again, the ones with a checkmark in the provided link are in the “recommended” set applied here)
- [plugin:@angular-eslint/recommended](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/src/configs/recommended.json)
- [plugin:@angular-eslint/template/process-inline-templates](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/src/configs/process-inline-templates.json) (required if you use inline templates in components)
- [prettier](https://github.com/prettier/eslint-config-prettier/blob/main/index.js) (turns off lint rules that may conflict with Prettier and must be the last entry)

And for **html ** files:

```
"files": ["*.html"],
"extends": [
  "plugin:@angular-eslint/template/recommended", 
  "plugin:@angular-eslint/template/accessibility"
 ],
```

- [plugin:@angular-eslint/template/recommended](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/src/configs/recommended.json)
- [plugin:@angular-eslint/template/accessibility](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/src/configs/accessibility.json)

In addition to these extended rules, you can add any rules you want in the rules object, and they will trump any extended rules. They can be:

eslint rules:

```
"no-underscore-dangle": ["error"]
```

typescript-eslint rules:

```
"@typescript-eslint/member-ordering": ["error"]
```

or angular-eslint rules:

```
"@angular-eslint/directive-selector": [
  "error",
  {
    "type": "attribute",
    "prefix": "mycompany",
    "style": "camelCase"
  }
]
```

Here’s a complete  **.eslintrc.json** file example:

```
{
  "root": true,
  "ignorePatterns": ["projects/**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates",
        "prettier"
      ],
      "rules": {
        "no-underscore-dangle": ["error"],
        "@typescript-eslint/member-ordering": ["error"],
        "@angular-eslint/directive-selector": [
          "error",
          {
            "type": "attribute",
            "prefix": "mycompany",
            "style": "camelCase"
          }
        ],
        "@angular-eslint/component-selector": [
          "error",
          {
            "type": "element",
            "prefix": "mycompany",
            "style": "kebab-case"
          }
        ]
      }
    },
    {
      "files": ["*.html"],
      "extends": ["plugin:@angular-eslint/template/recommended", "plugin:@angular-eslint/template/accessibility"],
      "rules": {}
    }
  ]
}
```

#### Prettier

Prettier doesn’t have as many configurable options as ESLint and that’s kinda the point. You can see the options that are available to be put in your  **.prettierrc.json** file [here](https://prettier.io/docs/en/options.html). I like these and I think they are common:

```
{
  "singleQuote": true,
  "trailingComma": "es5",
  "endOfLine": "auto",
  "bracketSameLine": true,
  "htmlWhitespaceSensitivity": "ignore"
}
```

### Sharing Configuration

Finally, I want to explore how you might share ESLint and Prettier configuration among all of your applications by publishing them as scoped npm packages.

#### ESLint

Create a scoped npm package with a **package.json** file named _@mycompany/eslint-config_:

```
{
  "name": "@mycompany/eslint-config",
  "version": "1.0.0",
  "main": "typescript.json",
  "peerDependencies": {
    "@angular-eslint/builder": ">= 15",
    "@angular-eslint/eslint-plugin": ">= 15",
    "@angular-eslint/eslint-plugin-template": ">= 15",
    "@angular-eslint/schematics": ">= 15",
    "@angular-eslint/template-parser": ">= 15",
    "eslint": ">= 8",
    "eslint-config-prettier": ">= 8.8.0",    
    "@typescript-eslint/parser": ">= 5.59.6"
  }
}
```

Note the peer dependencies which are required by the consuming application. We have used >= to allow applications to use newer versions. Applications will want to install the @angular-eslint packages that correspond to their Angular version.

I like to have one file for TypeScript rules and one for HTML rules.

In the **typscript.json** file below, the “extends” and “rules” objects are copied directly from the local file shown earlier:

```
{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@angular-eslint/template/process-inline-templates",
    "plugin:@angular-eslint/recommended",
    "prettier"
  ],
  "rules": {
    "@angular-eslint/directive-selector": [
      "error",
      {
        "type": "attribute",
        "prefix": "app",
        "style": "camelCase"
      }
    ],
    "@angular-eslint/component-selector": [
      "error",
      {
        "type": "element",
        "prefix": "app",
        "style": "kebab-case"
      }
    ],
    "@typescript-eslint/member-ordering": ["error"],
    "no-underscore-dangle": ["error"]
  }
}
```

In **html.json** , we have again copied from the local file shown earlier:

```
{
  "extends": [
    "plugin:@angular-eslint/template/recommended", 
    "plugin:@angular-eslint/template/accessibility"
  ]
}
```

Publish this package to npm or your company’s local package repo.

Then in the consuming application, install the package, and in the . **eslintrc.json** file, replace all “extends” entries with just these two:

```
{
  "root": true,
  "ignorePatterns": ["projects/**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "extends": ["@mycompany/eslint-config/typescript"]
    },
    {
      "files": ["*.html"],
      "extends": ["@mycompany/eslint-config/html"]
    }
  ]
}
```

Note how we extend from typescript for .ts files and html for .html files.

#### Prettier

For _Prettier_, the package name is _@mycompany/prettier-config_ and the **package.json** looks like this:

```
{
  "name": "@mycompany/prettier-config",
  "version": "1.0.0",  
  "main": "index.json"
}
```

and the **index.json** file is just like a  **.prettierrc.json** file:

```
{
  "singleQuote": true,
  "trailingComma": "es5",
  "endOfLine": "auto",
  "bracketSameLine": true,
  "htmlWhitespaceSensitivity": "ignore"
}
```

Publish this package to npm or your company’s local package repo.

Then in your application, install the package, delete the local  **.prettierrc.json** file, and add this (at the top level) to the **package.json** file:

```
"prettier": "@mycompany/prettier-config",
```

That’s it! Now your application is configured to use _ESLint_ for code quality checks, with all of the recommended rules from eslint, typescript-eslint and angular-eslint, and _Prettier_ for code formatting.

If you publish the configuration as npm packages, you can easily share that configuration with all of the Angular applications in your company.

### Stray Thoughts

- Beware .editorconfig and VS Code settings. Prettier will use both of these in the absence of a .prettierrc.json file, so it can be confusing when you’re trying to figure out where settings are coming from. You can see what Prettier is doing by looking in the OUTPUT tab and selecting Prettier from the dropdown.
- I used to use _eslint-plugin-prettier_ to run Prettier as a lint rule, but I don’t think it adds value anymore, and Prettier no longer recommends it.
- I used Angular 16 for these examples

### Bibliography

- [https://eslint.org/](https://eslint.org/)
- [https://typescript-eslint.io/](https://typescript-eslint.io/)
- [https://github.com/angular-eslint/angular-eslint/tree/main](https://github.com/angular-eslint/angular-eslint/tree/main)
- [https://prettier.io/docs/en/install.html](https://prettier.io/docs/en/install.html)
- [https://prettier.io/docs/en/editors.html](https://prettier.io/docs/en/editors.html)
- [https://prettier.io/docs/en/configuration.html#editorconfig](https://prettier.io/docs/en/configuration.html#editorconfig)
- [https://prettier.io/docs/en/api.html#prettierresolveconfigfilepath--options](https://prettier.io/docs/en/api.html#prettierresolveconfigfilepath--options)
- [https://prettier.io/docs/en/comparison.html](https://prettier.io/docs/en/comparison.html)
- [https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [https://github.com/prettier/eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)