ESLint with Gatsby

ESLint with Gatsby

How to use and customize ESLint with Gatsby

ByMario Kandut

honey pot logo

Europe’s developer-focused job platform

Let companies apply to you

Developer-focused, salary and tech stack upfront.

Just one profile, no job applications!

I have great news for you, my linting friend. Gatsby ships with built-in ESLint setup. According to the Gatsby docs, the provided eslint config is suitable for most of its users.

So why customize then? Well, there could be several reasons for customization of the "standard" ESLint setup, for example:

  • project requirements
  • company requirements
  • internal testing
  • personal preferences, etc.

💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.

If you want to know more about Linting and if ESLint is the right choice, have a look at this article What is Linting.

How to customize ESLint

I assume, you’ll start with customizing from the ESLint config Gatsby ships with, so you can then add additional presets, plugins, and rules.

See below the ESLint provided by the Gatsby starter or on github.

module.exports = {
  extends: [
  plugins: [`graphql`],
  rules: {
    // New versions of react use a special jsx runtime that remove the requirement
    // for having react in scope for jsx. Once the jsx runtime is backported to all
    // versions of react we can make this always be `off`.
    // I would also assume that eslint-config-react-app will switch their flag to `off`
    // when jsx runtime is stable in all common versions of React.
    'react/react-in-jsx-scope': usingJsxRuntime
      ? `off`
      : `error`, // Conditionally apply for reactRuntime?
    'import/no-webpack-loader-syntax': [0],
    'graphql/template-strings': [
        env: `relay`,
        schemaString: printSchema(schema, {
          commentDescriptions: true,
        tagName: `graphql`,
    'react/jsx-pascal-case': `off`, // Prevents errors with Theme-UI and Styled component
    'jsx-a11y/accessible-emoji': `warn`,
    'jsx-a11y/alt-text': `warn`,
    'jsx-a11y/anchor-has-content': `warn`,
    'jsx-a11y/anchor-is-valid': `warn`,
    'jsx-a11y/aria-activedescendant-has-tabindex': `warn`,
    'jsx-a11y/aria-props': `warn`,
    'jsx-a11y/aria-proptypes': `warn`,
    'jsx-a11y/aria-role': `warn`,
    'jsx-a11y/aria-unsupported-elements': `warn`,
    // TODO: It looks like the `autocomplete-valid` rule hasn't been published yet
    // "jsx-a11y/autocomplete-valid": [
    //   "warn",
    //   {
    //     inputComponents: [],
    //   },
    // ],
    'jsx-a11y/click-events-have-key-events': `warn`,
    'jsx-a11y/control-has-associated-label': [
        ignoreElements: [
        ignoreRoles: [
        includeRoles: [`alert`, `dialog`],
    'jsx-a11y/heading-has-content': `warn`,
    'jsx-a11y/html-has-lang': `warn`,
    'jsx-a11y/iframe-has-title': `warn`,
    'jsx-a11y/img-redundant-alt': `warn`,
    'jsx-a11y/interactive-supports-focus': [
        tabbable: [
    //"jsx-a11y/label-has-for": `warn`, was deprecated and replaced with jsx-a11y/has-associated-control in v6.1.0
    'jsx-a11y/label-has-associated-control': `warn`,
    'jsx-a11y/lang': `warn`,
    'jsx-a11y/media-has-caption': `warn`,
    'jsx-a11y/mouse-events-have-key-events': `warn`,
    'jsx-a11y/no-access-key': `warn`,
    'jsx-a11y/no-autofocus': `warn`,
    'jsx-a11y/no-distracting-elements': `warn`,
    'jsx-a11y/no-interactive-element-to-noninteractive-role': `warn`,
    'jsx-a11y/no-noninteractive-element-interactions': [
        body: [`onError`, `onLoad`],
        iframe: [`onError`, `onLoad`],
        img: [`onError`, `onLoad`],
    'jsx-a11y/no-noninteractive-element-to-interactive-role': `warn`,
    'jsx-a11y/no-noninteractive-tabindex': `warn`,
    'jsx-a11y/no-onchange': `warn`,
    'jsx-a11y/no-redundant-roles': `warn`,
    'jsx-a11y/no-static-element-interactions': `warn`,
    'jsx-a11y/role-has-required-aria-props': `warn`,
    'jsx-a11y/role-supports-aria-props': `warn`,
    'jsx-a11y/scope': `warn`,
    'jsx-a11y/tabindex-no-positive': `warn`,

First we need the necessary ESLint dependencies.

npm install --save-dev eslint-config-react-app

Then create a file .eslintrc.js through your editor/IDE or in the terminal touch .eslintrc.js and copy the code snippet below in the file.

module.exports = {
  globals: {
    __PATH_PREFIX__: true,
  extends: `react-app`,

That's it. Now you are ready to add all the additional presets, plugins and rules your project requires.

Keep in mind Gatsby specifics for the ESLint-file

  1. NO ESLint file: Gatsby adds by default a ESLint loader, which pipes all the feedback from ESLint into your terminal/browser console.
  2. YES there is a ESLint-file: When you add a custom .eslintrc file, Gatsby gives you full control over the ESLint configuration. This means also it overrides the built-in eslint-loader and no default rules will be loaded.

Customizing ESLint

You have now several options:

  • Now just copy your project specific rules in the .eslintrc and that's it.
  • If you want to start from scratch and apply your own linting rules, I recommend copying the rules from Gatsby and modify from that point on.
  • Another option would be to configure ESLint from scratch. The docs from ESLint have a great user guide for Configuring ESLint.
  • One way to customize the Gatsby ESLint is by using the community plugin gatsby-plugin-eslint.

Using gatsby-plugin-eslint

I recommend this plugin, have a look at the plugin page or github.

  1. Install the gatsby-plugin-eslint plugin npm install --save-dev gatsby-plugin-eslint
  2. Install ESLint and eslint-loader: npm install --save-dev eslint eslint-loader
  3. Create a .eslintrc file in your project root.
  4. Add the plugin to gatsby-config.js.
module.exports = {
  plugins: ['gatsby-plugin-eslint'],
  1. Use the options of the plugin to specify the rules.

Options for gatsby-plugin-eslint

The default options are:

  1. Lint .js and .jsx files.
  2. Exclude node_modules, .cache, and public folders from linting.
  3. Only lints in development in the 'develop' stage.
  4. Default ESLint-Loader options.

Customize the linting filetypes, exclusions, and ESLint-Loader options:

      resolve: 'gatsby-plugin-eslint',
      options: {
        test: /\.js$|\.jsx$/,
        exclude: /(node_modules|.cache|public)/,
        stages: ['develop'],
        options: {
          emitWarning: true,
          failOnError: false

An easy way to start linting is with these two linting plugins.


React specific linting rules for ESLint.

  1. Install the eslint-plugin-react

npm install --save-dev babel-eslint eslint-plugin-import eslint-plugin-react

  1. Update .eslintrc file:
  "parser": "babel-eslint",
  "rules": {
    "strict": 0
  "extends": [

AirBnB’s eslint-config-airbnb Linting

This package provides the eslint-config .eslintrc as an extensible shared config from Airbnb.

  1. Install eslint-config-airbnb

npm install --save-dev eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react

  1. Add or update .eslintrc:
  "extends": "airbnb"

Disabling ESLint

Just to be feature-complete with this blogpost, and I don't recommend this, but you can do it, if you really want to disable ESLint. Just create an empty .eslintrc file, and it will disable the eslint-loader and with it all the imported lint rules.

Thanks for reading and if you have any questions, use the comment function or send me a message @mariokandut.

References (and Big thanks): Gatsby docs, Gatsby ESLint community plugin, SPeshov, ESLint

Scroll to top ↑