How to configure Prettier and VSCode

How to set up Prettier, ESLint and VSCode per project and have your JavaScript auto-formatted without pulling your hair.

You can configure JavaScript code auto-formatting with Prettier to work per-project. This allows you to get a consistent formatting without thinking or arguing about it. This blog post shows how to configure Prettier to work from command line, from VSCode and from Git hooks.

You can find the sample project with different Prettier settings configured per-subfolder at bahmutov/prettier-config-example.

Why Prettier?

Prettier reformats your JavaScript code consistently and (arguably) in way that is easy to read and understand. It takes whatever copy/pasted code snippets you put into your file and makes it look the same as the rest of the code. By using Prettier your team skips ALL disagreements about spacing, variable declarations, semi-colons, trailing commas, etc. The code just magically gets to the format you pick.

You can use Prettier from command line, or from your code editor whenever you paste or save a file. I prefer to use two solutions described in this blog post:

  • format the file from VSCode every time I save it
  • format the changed files on Git commit before committing them

Let me show you how to do both.

Setup

When setting up Prettier it is important to configure it per-project. Not every project uses the same code style, thus it is important to respect the style of whatever project you are currently working in. The demo repo bahmutov/prettier-config-example has two subfolders, each with its distinct code style, enforced by Prettier. In reality, each of your repos will have its style; I am using subfolders in order to keep the example simple.

I assume you are using NPM and have package.json file inside the repository. Install Prettier

1
npm install --save-dev --save-exact prettier

At the root of the project create the Prettier configuration file. In my example I have two subfolders, and there is a configuration file in each subfolder:

1
2
3
4
5
prettier-config-example/
projectA/
.prettierrc.json
projectB/
.prettierrc.json

I like using JSON configuration format so my code editor helps me. In fact, VSCode understands the Prettier configuration file format via the built-in json schema. So when I edit projectA/.prettierrc.json file, I get intelligent tooltips.

Picking trailing comma setting

Settings

Prettier tries to enforce the same code style without 100s of options, thus there are just a few settings you can change. Here are settings I am using in the first project to make it look "traditional" ES5

projectA/.prettierrc.json
1
2
3
4
5
6
{
"trailingComma":"none",
"tabWidth": 4,
"semi": true,
"singleQuote": false
}

The second project uses more modern style without semi-colons and with trailing commas.

projectB/.prettierrc.json
1
2
3
4
5
6
{
"trailingComma": "all",
"tabWidth": 2,
"semi": false,
"singleQuote": true
}

VSCode setup

To use the Prettier we have just installed from VSCode we need to install the Prettier VSCode extension:

  1. Launch VS Code Quick Open (Ctrl+P)
  2. Run the following command
1
ext install esbenp.prettier-vscode

Because you might have global settings related to code formatting, I prefer having in each repository a file with local workspace VSCode settings. I commit this file .vscode/settings.json to source control to make sure everyone uses the same extension to format the code.

.vscode/settings.json
1
2
3
4
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}

Now every time we save a JavaScript file, it will be formatted using Prettier automatically. Here is me formatting projectA/index.js file by saving it.

Prettier formats projectA/index.js

Notice the double quotes, semi-colons, etc - Prettier has applied the settings from projectA/.prettierrc.json. It also split long object across multiple lines to make it easier to read.

The same JavaScript code in projectB/index.js gets formatted by Prettier using different local settings and ends up looking different.

Prettier formats projectB/index.js

Single quotes, no semi-colons, trailing commas.

Tip: I love formatting code on "Save", but I hate formatting code on "Paste" - because it always adds extra line breaks. So I highly recommend the following VSCode settings

1
2
3
4
5
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnPaste": false
}

Format files from CLI

Formatting every file as you save it is nice, but we can also format ALL source files at once using Prettier CLI. In the package.json add a script to format files matching the mask and to write them back to disk.

package.json
1
2
3
4
5
6
7
8
9
{
"name": "prettier-config-example",
"scripts": {
"format": "prettier --write 'projectA/*.js' 'projectB/*.js'"
},
"devDependencies": {
"prettier": "1.18.2"
}
}

Run this NPM script and the files will be formatted to follow the Prettier style

1
2
3
4
5
6
7
$ npm run format

> [email protected] format /Users/gleb/git/prettier-config-example
> prettier --write 'projectA/*.js' 'projectB/*.js'

projectA/index.js 30ms
projectB/index.js 10ms

Format staged files on commit

Whenever we work with files locally, we might accidentally commit them without proper styling. That's where Git hooks and formatting staged files comes in handy. To consistently format all files before committing and then commit changes, I recommend using husky + lint-staged combination of tools.

1
2
3
$ npm i -D husky lint-staged
+ [email protected]
+ [email protected]

Now configure pre-commit hook to run Prettier against staged JavaScript files. In the package.json set the following

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"devDependencies": {
"husky": "3.0.5",
"lint-staged": "9.2.5",
"prettier": "1.18.2"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": ["prettier --write", "git add"]
}
}

See lint-staged code formatting documentation.

If you try to commit changed JavaScript files, they will automatically be formatted and re-staged, ensuring only pretty JavaScript code is committed. In the Git commit shortcut output below, the "Running tasks..." messages comes from the lint-staged tool.

1
2
3
4
5
6
7
$ g done "add husky and lint-staged"
husky > pre-commit (node v12.4.0)
↓ Stashing changes... [skipped]
→ No partially staged files found...
✔ Running tasks...
[master 583b92a] add husky and lint-staged
2 files changed, 1513 insertions(+)

Of course, you can skip the Git pre-commit hook by committing with -n flag.

Catch mis-formatted files on CI

Using stop-build

You can really enforce the formatting before pushing code to the central repository by running Prettier on CI and then detecting any changed files. Just run stop-build after running Prettier.

1
2
3
script:
- npm run format
- npx run stop-build

If any of the source files were reformatted by Prettier, the stop-only will detect changed source files using Git information and will exit with an error. It will list the changed files, something like this:

1
2
3
⚠️ there are 2 changed files
M projectA/index.js
M projectB/index.js

Using Prettier

Prettier has built-in command --check that validates code files against formatting. Using it from a script in package.json file:

1
2
3
4
5
{
"scripts": {
"check": "prettier --check 'projectA/*.js' 'projectB/*.js'"
}
}

Then on CI we can call the script right after npm install

1
2
3
script:
- npm install
- npx run check

Let's say one of the files has not been formatted.

1
2
3
4
5
6
7
8
$ npm run check

> [email protected] check /git/prettier-config-example
> prettier --check 'projectA/*.js' 'projectB/*.js'

Checking formatting...
projectB/index.js
Code style issues found in the above file(s). Forgot to run Prettier?

Common problems

Nothing happens on save

You are saving a file in VSCode ... and the code does not change. This could be due to three issues:

  1. Make sure local workspace settings have auto-format on save enabled. Open .vscode/settings.json file and confirm:
    • VSCode Prettier extension is configured as the default formatter.
    • Formatting on save is enabled
.vscode/settings.json
1
2
3
4
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
  1. Prettier extension might be disabled by VSCode. Make sure the word "Prettier" appears on the Status Bar and has check mark symbol next to it. To check:
    • Right click on the Status Bar. Make sure the "Prettier" extension appears there is displayed.

Show Prettier extension status

  1. Make sure there is a checkmark next to the "Prettier" in the Status Bar. Sometimes after enabling the extension, it is loaded, but not enabled.

Prettier extension is disabled for some reason

One thing I have noticed that sometimes saving a file enables Prettier if the .vscode/settings.json have the extension enabled for this workspace. For example in this animation I am saving the file with double quotes around a string, and magically the Prettier extension gets the check mark and does its job. Don't ask.

Saving file enables Prettier and formats the code

If everything else fails, quit VSCode and start it again.

Code formatting is wrong

Here is a little animation that shows a file being saved with Prettier setting "trailingComma: true", yet the comma gets deleted somehow.

Saving file removes trailing comma

Check if there are OTHER code formatting extensions installed and disable them for this workspace. For some reason, VSCode can use globally installed extension overwriting local setting. Don't ask. In my case, I had "Prettier-Standard" extension enabled globally. After disabling the "Prettier-Standard" for the current workspace, Prettier extension started working as expected.

Disabling Prettier-Standard extension

Why can't VSCode save the list of disabled extensions in .vscode/settings.json?

Tips

Ignoring files

Sometimes you have files that should not be formatted: auto-generated source files, saved snapshots, etc. You can list file masks to ignore in file .prettierignore. For example, to ignore all JavaScript files in snapshots folders use

.prettierignore
1
2
3
# do not run Prettier against JavaScript files
# in "snapshots/" folders
**/snapshots/*.js

Saving without formatting

If you ever work in someone else's project, please respect their formatting. In order to avoid reformatting the entire file when you save it from VSCode, save it without formatting. Run "Command + Shift + P" to open the Command Palette and type "save without" until you see "File: Save without Formatting" command - use that.

Save without formatting

Temporarily disable formatting

There is also an extension that temporarily disables format on save feature called Formatting Toggle. Install it in your VSCode and whenever you want to temporarily disable Prettier on save, click on the "Formatting" toggle in the status bar.

Save without formatting

Only format configured projects

In the VSCode global settings, set this option to only allow running Prettier in the folders with Prettier config file.

1
2
Prettier: Require Config
✅ Require a 'prettierconfig' to format

Use Eslint with Prettier

Prettier reformats JavaScript code to follow certain style, it does not check the meaning of the code. For example, Prettier happily reformats the following wrong code.

Prettier makes this wrong code look pretty

Static linters, like ESLint can catch the assignment to a constant variable, so we need both:

  • Prettier will reformat the code to be consistent in style
  • ESLint will analyze the meaning of code and catch potential problems

Disable style rules in ESLint

ESLint runs a long list of rules against the code, and some of these rules are stylistic, and can conflict with Prettier's style. Thus we need to configure ESLint to skip those rules. This configuration is in module eslint-config-prettier. Install it

1
$ npm i -D eslint eslint-config-prettier

and can be added to your project .eslintrc.json file. ESLint will not run without a valid configuration file.

1
2
3
4
5
6
{
"env": {
"es6": true
},
"extends": ["eslint:recommended", "prettier"]
}

Now when you run ESLint against this file

projectC/index.js
1
const name = 'Joe'; name = 'Mary'

Then ESLint will catch the const assignment error; it will also catch that the variable name is never used after assignment.

1
2
3
4
5
6
7
$ npx eslint projectC/index.js

/prettier-config-example/projectC/index.js
1:7 error 'name' is assigned a value but never used no-unused-vars
1:21 error 'name' is constant no-const-assign

✖ 2 problems (2 errors, 0 warnings)

Integrate ESLint in VSCode

Since we are using VSCode, it makes sense to install ESLint VSCode extension called dbaeumer.vscode-eslint

Open Command Pallette with Command + P

1
ext install dbaeumer.vscode-eslint

Enable this extension in VSCode workspace settings

1
2
3
4
5
{
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true,
"eslint.enable": true
}

JavaScript files should now show ESLint errors right inside VSCode editor.

ESLint errors

You can see these errors for yourself by opening projectC/index.js in VSCode from the example repo.

Run Prettier from ESLint

Since ESLint can detect and fix many of the errors it detects automatically, let's tell ESLint to run Prettier too. Here is the recommended setup

Install ESLint Prettier config and plugin

1
$ npm i -D eslint-config-prettier eslint-plugin-prettier

Point ESLint at the recommended settings which include Prettier styles

projectD/.eslintrc.json
1
2
3
4
5
6
7
8
9
10
{
"env": {
"node": true,
"es6": true
},
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"rules": {
"no-console": "off"
}
}

Notice in the screenshot below how ESLint warnings in VSCode editor include style errors from Prettier.

ESLint shows Prettier errors

If we run ESLint with --fix flag, it will use Prettier to auto format code, solving both stylistic and semantic problems.

ESLint with Prettier fixes the code formatting

If you decide to use ESLint with Prettier rules and have configured husky to run lint-staged, point it at eslint --fix instead of prettier --write.

VSCode + ESLint + Prettier setup

Let's configure VSCode to use ESLint to auto-fix found issues, including Prettier. The workspace settings use dbaeumer.vscode-eslint.

1
2
3
4
5
6
7
{
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true,
"eslint.enable": true,
"eslint.alwaysShowStatus": true,
"eslint.autoFixOnSave": true
}

The animation shows how saving the file fixes both style and lint problems.

Using ESLint to auto-format and auto-fix issues on save

VSCode + ESLint + Prettier + TypeScript setup

ESLint can lint TypeScript files through typescript-eslint, and Prettier can format TypeScript code. Let's set it up.

First, if you have previous installed TSLint extension vscode-tslint for VSCode, uninstall it - let ESLint do everything.

Second, install a new parser and plugin modules

1
2
3
4
5
$ npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
+ @typescript-eslint/[email protected]
+ @typescript-eslint/[email protected]
updated 2 packages and audited 576 packages in 2.42s
found 0 vulnerabilities

Then set the VSCode workspace settings to lint TypeScript files

.vscode/settings.json
1
2
3
4
5
6
7
8
9
10
11
{
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true,
"eslint.enable": true,
"eslint.alwaysShowStatus": true,
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"typescript"
]
}

Set the ESLint options. Parsing files will be done using @typescript-eslint/parser, and we need @typescript-eslint plugin.

.eslintrc.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"prettier/@typescript-eslint"
],
"rules": {
"no-var": "error"
}
}

And now you should see ESLint + Prettier errors and warnings in VSCode

ESLint error - no vars allowed

Style error - no semi-colons allowed

Note: there is a bug in VSCode + ESLint extension where Prettier is not found. If you open Prettier console you can see the error, there is an open issue

Prettier cannot fix the style automatically

So we see the lint and style errors, yet cannot reformat the code automatically on save. To work around this issue, use NPM script command.

package.json
1
2
3
4
5
{
"scripts": {
"fix-ts": "eslint --fix 'project-with-typescript/*.ts'"
}
}

Run this command and it should reformat the TS files and fix most ESLint issues.

Fixing TypeScript files

Use Prettier + ESLint + Cypress

One final touch. If you write Cypress end-to-end tests, there is an official cypress-io/eslint-plugin-cypress plugin that can catch some common test mistakes. You can find an example "test" in project-with-Cypress/index.js file.

First, install the plugin

1
$ npm i -D eslint-plugin-cypress

Then extend ESLint settings

project-with-Cypress/.eslintrc.json
1
2
3
4
5
6
7
8
9
10
11
{
"env": {
"cypress/globals": true
},
"extends": [
"eslint:recommended",
"plugin:prettier/recommended",
"plugin:cypress/recommended"
],
"plugins": ["cypress"]
}

Let's say your test tries to get back an element using cy.get command.

project-with-Cypress/index.js
1
2
3
4
5
6
7
// typical Cypress test
it('loads todos', () => {
// this is wrong - Cypress commands are asynchronous
// you cannot get element back from cy.get
// see https://on.cypress.io/get
const myApp = cy.get('#my-app')
})

This WON'T work - cy.get does not return an element, like a Promise, the found element will be passed down the command chain. Notice how ESLint shows an error if you try to assign the value of the cy.get command.

ESLint shows Cypress error

Format other languages with Prettier

Prettier can format many languages: JavaScript, JSON, Markdown, HTML, CSS, etc. Here is formatting CSS for example.

Format CSS file using Prettier

Format JSON files with Prettier

You can configure Prettier and its VSCode extension to format your JSON files. Since there is already a default JSON formatter built into VSCode, you need to tell VSCode to specifically use esbenp.prettier-vscode to format JSON files. Here is a sample project settings file.

.vscode/settings.json
1
2
3
4
5
6
7
8
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true,
"json.format.enable": false
}

Format JSON files using Prettier

Use custom settings overrides

Here is a nice feature - you can set custom Prettier settings for some files. For example, we can use 2 spaces to indent by default, but 4 spaces to indent code blocks inside Markdown files, and 6 spaces to indent JSON files. Just because.

.prettierrc.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"trailingComma": "all",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"overrides": [
{
"files": "*.md",
"options": {
"tabWidth": 4
}
},
{
"files": "*.json",
"options": {
"tabWidth": 6
}
}
]
}

Let's save a JSON file.

Format JSON files using Prettier with overridden settings

And here is saving a Markdown with a code block - which gets automatically formatted using Prettier with 4 spaces per tab.

Format code blocks inside Markdown files

Chrome extension

There is now Chrome Prettier extension that can format code blocks in text areas. Seems for now it is limited to StackOverflow and GitHub.

Run Prettier inside GitHub Action

GitHub Actions are now generally available - and they can do lots of interesting things, including running Prettier on every push and if there are any changes, committing the code and pushing it to the repo. In essence, they are doing the hard work for you! Read Trying GitHub Actions blog post for full details, here is the relevant CI YML file from bahmutov/gh-action-with-prettier repo.

.github/workflows/ci.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name: Prettier
on: [push]
jobs:
build:
name: Prettier
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- uses: bahmutov/[email protected]
- run: npm run format
- run: git status
# commit any changed files
# https://github.com/mikeal/publish-to-github-action
- uses: mikeal/[email protected]
env:
# github token is automatically injected by GH Action
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Beautiful, and on every push, if there are any format changes, the code gets updated and pushed, which you can see from the list of commits.

Prettier inside GitHub Action fixing code