loading...

How To Set Up ESLint, TypeScript, Prettier with Create React App

benweiser profile image Ben Weiser Updated on ・3 min read

Note: CRA 3.0 will include TS linting with ESLint out of the box, https://github.com/facebook/create-react-app/issues/6475

I recently learned that TSLint was soon to be deprecated (cue sad violin music), but for very good reason as the JS/TS community starts to converge around a common set of core technologies. If you're interested in learning about these reason Palantir (creator of TSLint) has a nice read for you over here.

Alas the time had come for me to switch my small personal project to TypeScript. I've been a full time TypeScript dev for the past year and found that I miss it dearly even on smaller JavaScript projects. I used to opt for plain old JS to reduce complexity, but at this point my knowledge of TypeScript makes it fairly effortless to configure on even the simplest projects. Not to mention that the latest version of Create React App support TypeScript integration right out of the box!

I've set up TSLint quite a few times in the past, but knowing that ESLint was the future of TypeScript linting I decided to give it a go. Set up wasn't as straight forward as I would've hoped given that things are in quite a state of flux at the moment presented some challenges due to lack of documentation so I've decided to document here.

This article assumes you're using the latest version of CRA which comes with ESLint already out of the box.

First, let's install or devDependencies

npm i -D @types/react @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-config-react eslint-plugin-prettier prettier

Your devDependecies in package.json should now look like this,

  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^1.6.0",
    "@typescript-eslint/parser": "^1.6.0",
    "eslint-config-prettier": "^4.1.0",
    "eslint-config-react": "^1.1.7",
    "eslint-plugin-prettier": "^3.0.1",
    "prettier": "^1.16.4"
  }

Now create two files in your projects root (same level as your src folder).

.eslintignore
.eslintrc.json

You might see some tutorials use .yml or .js configurations and some tutorials might exclude an .eslintignore all together and use pattern matching in their node js scripts to exclude certain folders. All of these approaches are valid and it's really a matter of preference which one you decide to use.

In your .eslintrc.json add

{
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended"
  ],
  "plugins": ["react", "@typescript-eslint", "prettier"],
  "env": {
    "browser": true,
    "jasmine": true,
    "jest": true
  },
  "rules": {
    "prettier/prettier": ["error", { "singleQuote": true }]
  },
  "settings": {
    "react": {
      "pragma": "React",
      "version": "detect"
    }
  },
  "parser": "@typescript-eslint/parser"
}

and in your .eslintignore add any paths you don't want linted. In my case, I want to exclude my tests folders and the service worker that is packaged with CRA

src/registerServiceWorker.js
src/**/__tests__/**

In your package.json file we're going to add a new scripts file that will allow us to run our linter. Next to your react start, build, and test scripts add

"lint:fix": "eslint './src/**/*.{ts,tsx}'",

our next step assuming we've built a brand new Create React App project is to create our first TypeScript file. Go ahead and rename App.js to App.tsx and run npm start in your terminal. CRA will detect this is a TypeScript project and automatically add a tsconfig.json file for you. Running npm run lint will now get you output of your linted code within your terminal window. If you're using VSCode install the ESLint extension to get in editor highlighting. Now open up your App.tsx file and it should look like this
App tsx code
Hovering your mouse over the render method should reveal two ESLint errors specific to TypeScript.

Missing accessibility modifier on method definition render.eslint(@typescript-eslint/explicit-member-accessibility)
Missing return type on function.eslint(@typescript-eslint/explicit-function-return-type)

In editor highlighting will let you know if your code is violating the configured linter rules without having to explicitly run the lint script. If we want to disable these rules we can add

   "@typescript-eslint/explicit-member-accessibility": 0,
   "@typescript-eslint/explicit-function-return-type": 0,

to our rules configuration in eslintrc.json. This is where we can disable rules, enable new rules and customize the default configuration that we've extended. In some cases certain linting issues can be auto-corrected by appending --fix to npm run lint.

If using VSCode in your settings.json add the following to enable auto-fix on save,

  "eslint.validate": [
    "javascript",
    "javascriptreact",
    {
      "language": "typescript",
      "autoFix": true
    },
    {
      "language": "typescriptreact",
      "autoFix": true
    }
  ],

Posted on by:

benweiser profile

Ben Weiser

@benweiser

I'm a full stack web engineer with a strong front-end focus. I'm passionate about making the user's experience on the web more accessible, faster, and easier to use.

Discussion

markdown guide
 

Hey, how did you deal with the instance of ESLint that's built into CRA?

I've tried what you've done here previously, but CRA was detecting a "conflicting" version of ESLint and crashing. In fact, not so long ago, the CRA team mentioned it explicitly, that there's no way to set up a custom ESLint config yet. Has that been fixed or have you found a different approach?

 

I always had problems when a Eslint + Prettier tutorial tells you to add the eslint package along with the prettier stuff in a Create React App (CRA) project. Doing this lately tends to install Eslint 7 and all kinds of errors crop up due to a version conflict with the eslint used internally by the CRA scripts (version 6.6.0 I believe). This is why Ben's tutorial works, it simply gets the Prettier and Prettier packages up and running to work with the version of eslint that CRA and the CRA scripts use out of the box.

I suspect if one really wants the latest Eslint installed, you'd have to run the eject script and then dive in to the CRA config files to change out the eslint versions.

 

I read things more carefully and realized I misunderstood what exactly CRA was complaining about. It didn't like the fact that I installed a different version of ESLint, but once I swapped it for CRA's version, it was fine.

However, I noticed that you don't install ESLint in this article at all? How are you calling it in your scripts then? Is it installed globally?

 

You get ESLint for free when you install CRA so there's no need to install it in your package.json. I have experienced the same warning you're describing here when attempting to install my own version of ESLint. I uninstalled ESLint in my package.json and everything seems to be running as expected. I'm not sure regarding the restriction of ESLint configs, I have always been able to successfully set up lint configs though in the past opted for TSLint

It's worth nothing that this configuration was tested with the following versions of react/react-dom and react scripts

    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "2.1.8"

It could work to just keep using ESLint installed through CRA, but the VSCode ESLint extension doesn't seem to pick it up, of it's not listed among your dependencies. Plus, I don't think it's a good practice to reference a dependency in your code that you don't install manually... But can work for sure.

I agree, there should be some concern over this if a dependency higher up in the tree you don't have control over happens to change than dependent libraries downline could potentially cause issues. This is also the case when using any libraries that depend on Jest as well which also comes prepackaged with CRA. Given that these libraries are the new standard for TS linting with ESLint with full community support my hope is that there will be reasonable support for the latest versions of ESLint. I will mention up above as well this will be solved with CRA 3.0 that will include these libraries OOTB.

 

Before running "npm start" ensure you "npm installl typescript --save-dev".

Eslint doesn't work for me. The lint command fails with.

> eslint './src/**/*.{ts,tsx}'


Oops! Something went wrong! :(

ESLint: 5.16.0.
No files matching the pattern "'./src/**/*.{ts,tsx}'" were found.
Please check for typing mistakes in the pattern.

Both my .eslintignore and .eslintrc.json are identical copy and paste. I used "npx create-react-app eslint-integration-typescript-example"


{
    "extends": [
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:@typescript-eslint/recommended",
        "prettier/@typescript-eslint",
        "plugin:prettier/recommended"
    ],
    "plugins": [
        "react",
        "@typescript-eslint",
        "prettier"
    ],
    "env": {
        "browser": true,
        "jasmine": true,
        "jest": true
    },
    "rules": {
        "prettier/prettier": [
            "error",
            {
                "singleQuote": true
            }
        ]
    },
    "settings": {
        "react": {
            "pragma": "React",
            "version": "detect"
        }
    },
    "parser": "@typescript-eslint/parser"
}
{
  "name": "tslint-integration-example",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "3.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "lint": "eslint './src/**/*.{ts,tsx}'"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@types/react": "^16.8.18",
    "@typescript-eslint/eslint-plugin": "^1.9.0",
    "@typescript-eslint/parser": "^1.9.0",
    "eslint-config-prettier": "^4.3.0",
    "eslint-config-react": "^1.1.7",
    "eslint-plugin-prettier": "^3.1.0",
    "prettier": "^1.17.1",
    "typescript": "^3.4.5"
  }
}
src/registerServiceWorker.js
src/**/__tests__/**
$ create-react-app --version
3.0.1

I can see that the file does have linting suggestions in the UI. But "npm run lint" doesn't work

Would really appreciate some guidance. This is the third guide I've tried to follow, each time with a fresh "CRA"

  1. dev.to/robertcoopercode/using-esli...
  2. javascriptplayground.com/typescrip...

It works fine with standard javascript and I managed to get that to work as shown in this repo github.com/gruckion/eslint-integra... EDIT < doesn't work here either

The currently broken repo is here, github.com/gruckion/eslint-integra....

 

If you are having the same issue I was the cause is;

"lint": "eslint './src/**/*.{ts,tsx}'",

In order to get this to work, I had to remove the single quotes.

I am not sure why the single quote approach doesn't work for me. I read over the documentation and found it is called "glob" notation and it depends on the console your using (I am using git bash).

Placing your glob in double quotes will use nodes glob format. If you want to test it and don't want to globally install eslint use;

npx eslint './src/**/*.{ts,tsx}'

This works fine for me. with and without double or single quotes.

eslint.org/docs/user-guide/command...

If anyone has any idea why I can't run the script from my package.json with single quotes I would love to know.

 

It's an oddity of the operating system, I bet you were working with a windows 10 machine.

I got the exact same error, this is what I did.

lint: "eslint \"./src/*/.{ts,tsx}\""

stackoverflow.com/questions/481043...

thank you. It works in Win10 Pro now.

 

I can't get the prettier to run for some rules. One of them is "jsx-max-props-per-line".

This is how I specified -

"rules": {
        "prettier/prettier": [
            "error",
            {
                "singleQuote": true,
                "jsx-max-props-per-line": [
                    1,
                    {
                        "maximum": "1",
                        "when": "always"
                    }
                ]
            }
        ],
        "react/jsx-max-props-per-line": [
            1,
            {
                "maximum": "1",
                "when": "always"
            }
        ]
    },

Tried almost every permutation and combination. Please note that I only have eslint plugin on vscode enabled. No other linters are present in editor. Can someone please help!

 

This is due to 'printWidth' property in the .prettierrc configuration file. By default, it's 120. For me, 80 works great and shows props in a new line.

module.exports = {
semi: true,
trailingComma: "all",
singleQuote: true,
printWidth: 80,
tabWidth: 4
}

prettier.io/docs/en/options.html#p...

 

I have the same problem. Have you found a solution?

 

Thanks to your guide I was able to fix the CRA's own eslint that was failing on as with Parsing error: Unexpected token, expected "," by adding theses lines to package.json.

{
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^1.6.0",
    "@typescript-eslint/parser": "^1.6.0",
    "eslint-config-react": "^1.1.7"
  },
  "eslintConfig": {
    "parser": "@typescript-eslint/parser",
    "plugins": ["react", "@typescript-eslint"],
    "extends": ["react-app"],
    "rules": {
      // custom rules here
    }
  }
}
 

Why are you using your own eslint related files instead of the eslintConfig that is already present in package.json? Did you remove the config from package.json or do you keep it there, having two eslint configurations? Is your solution still using CRA's internal mechanisms or is it something completely custom?

 

I'm not using an eslintConfig in my package.json. Using the .eslintrc.json is the only place I have added any configurations. This is just my preference, eslintConfig in package.json is also a valid approach. It just depends on how you like to structure your project

 

I keep it simple and short.

"prettier": {
    "configJS": {
      "printWidth": 60,
      "tabWidth": 2,
      "bracketSpacing": true
    },
    "configJSON": {
      "parser": "json",
      "printWidth": 60,
      "tabWidth": 2,
      "bracketSpacing": true
    }
  },

don't forget to make it ugly after you make it pretty :) Thank you for sharing, I was able to learn a few things to tweak my formatting. enjoy :)

npmjs.com/package/uglify-js

"uglify": {
    "parse": {
      "html5_comments": false,
      "shebang": false
    },
    "compress": {
      "dead_code": true,
      "drop_debugger": true,
      "booleans": true,
      "loops": true,
      "unused": true,
      "toplevel": true,
      "top_retain": true,
      "hoist_vars": true,
      "if_return": true,
      "inline": true,
      "join_vars": true,
      "reduce_vars": true,
      "warnings": true,
      "drop_console": true,
      "passes": 3,
      "ecma": 6
    },
    "mangle": {
      "toplevel": true,
      "eval": true
    },
    "output": {
      "preamble": "/* Copyright © 2020 DreamScale, Inc // Torchie Shell // Author : ZoeDreams <zoe@dreamscale.io> */",
      "bracketize": true,
      "ecma": 6,
      "indent_level": 0,
      "max_line_len": false,
      "preserve_line": false,
      "quote_keys": false
    },
    "ecma": 6,
    "nameCache": null,
    "ie8": false,
    "warnings": true
  }
 

Hey Ben,

Thanks for putting this post together.

Something didn't really work for me or I expected something it was not supposed to offer.

I have a similar configuration to what you are suggesting and if I run npx eslint src/App.tsx, I get:

6:9 error 'chaire' is assigned a value but never used @typescript-eslint/no-unused-vars

Notice that it is an "error". When I run npm start, react-script kicks in and as the app is starting I get (in my command line window):

Compiled with warnings.

./src/App.tsx
Line 6: 'chaire' is assigned a value but never used @typescript-eslint/no-unused-vars

Note that it is a warning and therefore isn't respecting my eslint configuration. And I really want it to be an error!!! :)

Here's my lint config:

module.exports = {
"extends": [
"react-app",
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"plugins": [
"react",
"@typescript-eslint",
"prettier"
],
"env": {
"browser": true,
"jasmine": true,
"jest": true
},
"rules": {
"prettier/prettier": [
"error", {
"singleQuote": true,
"tabWidth": 4
}
],
"@typescript-eslint/no-unused-vars": ["error", {
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": false
}]
},
"settings": {
"react": {
"pragma": "React",
"version": "detect"
}
},
"parser": "@typescript-eslint/parser"
}

Regards,
Bruno

 

I copied the project to another folder, node_modules and all (no changes). Turns out now I get an error when react-scripts start runs:

TypeScript error in C:/BitB/auth-ui/src/App.tsx(6,11):
'chaire' is declared but its value is never read. TS6133

Looks like a typescript error and not a lint error, but I'll take it :)

However, I tried with a less problematic rule "semi": "error" and in my code import './App.css' without semicolon.

npx eslint src/App.tsx shows:

25:2 error Missing semicolon semi

But react-scripts start doesn't care about it.

Any help would be appreciated.

 

Hi!
I believe there is an error with lint:fix command.
It is not the fix :) It should look like this:

"lint:fix": "eslint --fix './src/**/*.{ts,tsx}'"