Graphic showing the GitLab and ESLint logos side-by-side

ESLint statically analyses JavaScript to identify issues before the code is run. It can also find and fix stylistic discrepancies, helping your project align with any style guides you follow.

Creating a comprehensive ESLint config can take a couple of hours. Over one hundred rules are available and you need to decide which ones your team will use. ESLint ships with a “recommended” set of best practice rules; many other useful rules are not turned on by default.

In this guide, we’ll show how to write an ESLint config once and share it with your team using a private npm registry in GitLab. You’ll be able to reuse your ESLint config across all your projects by referencing your registry package. We’ll be skipping over the basics – it’s assumed you’ve got some experience with creating new GitLab groups and projects.

Getting Started

ESLint makes it easy to use shared rules. Any npm package is eligible to become an ESLint plugin. The package’s entrypoint needs to export an ESLint configuration object.

Begin by creating a new GitLab project to hold your ESLint configuration. Clone the repository down to your machine. Next, add a package.json to describe your npm package:

    "name": "@example-group/eslint-config",
    "author": "Example Author",
    "description": "An example description",
    "version": "1.1.0",
    "main": ".eslintrc.js",
    "peerDependencies": {
        "eslint": ">=7"

Take note of the format for the package name. This must match your project’s GitLab namespace name (@group/project). The @ symbol creates a package scope. We’ll use this later to instruct npm to fetch @group packages from our private npm registry. If you’ve got a complex namespace name, refer to the GitLab documentation to work out the correct scope name to use.

The main field must be set to the file which will contain your ESLint configuration. We’re using .eslintrc.js for this article. ESLint is specified as a peer dependency. This means projects using your library must include ESLint in their own dependencies.

Creating Your ESLint Configuration

Create your actual ESLint configuration file next. Make sure you use the file you specified as main in package.json.

Here’s a basic example:

module.exports = {
    "extends": ["eslint:recommended"],
    "rules": {
        "comma-dangle": ["error", "never"],
        "indent": ["error", "tab", {"SwitchCase": 1}],
        "max-classes-per-file": ["error", 1]

This configuration is based on ESLint’s built-in recommended rules. This gives you a good starting point to layer your own rules onto. We’ve added three extra rules to ban dangling commas, limit each file to one named class, and force the use of tabs instead of spaces.

Authenticating to Your npm Registry

Now you’re ready to publish your package to your private npm registry in GitLab. You’ll need to create a GitLab API token first – click your profile icon in the top-right, then the “Preferences” menu item. Select “Access Tokens” from the sidebar. Create a new access token with the read_registry and write_registry scopes. Note down the token value which will be displayed – you won’t be able to retrieve it in the future.

image of creating an API access token in GitLab

Next you need to connect npm to your registry:

npm config set @example-group:registry
npm config set -- '//' "$API_TOKEN"

Replace $API_TOKEN with the API access token you generated within GitLab. The first command configures npm to fetch any package in the @example-group scope from your private registry. The second command provides npm with the registry authentication token.

npm lets you add any number of package scopes. Each private scope needs its own authentication token. If you work in multiple GitLab groups, you’ll need to define a scope for each one.

Publishing to GitLab’s npm Registry

The authentication configured above applies to the instance-level GitLab npm registry. This is ideal for installing dependencies as you can access packages in any group you’ve got access to, without manually specifying project IDs.

To publish packages, you must use the project-level API endpoint. This requires separate authentication within npm. You can reuse the same API token, if it’s got write_registry permissions for your project:

npm config set -- '//<id>/packages/npm/:_authToken

Replace <id> in the registry URL with the ID of the GitLab project you’re publishing to. You can find this on the project’s homepage, displayed next to its name.

image of a project ID in GitLab

Next, update your package.json with a publishConfig object. This instructs npm publish to submit your package to your registry, instead of the public registry on

    "publishConfig": {
        "@example-group:registry": "<id>/packages/npm/"

Run npm publish to publish your package to your GitLab registry! You should see your package show up under “Packages & Registries” within the GitLab interface.

image of GitLab package registry

Installing Packages From Your Registry

mpm commands such as npm install, npm ci and npm outdated should work without further configuration. npm will automatically consult GitLab to determine the latest version of your private packages. Files will then be downloaded directly from your registry.

No authentication is needed to install packages from public projects. If your project’s private, you’ll need to provide npm with a GitLab API token as described in the preceding sections. Using a project-level endpoint will let you install that project’s packages; an instance-level endpoint lets you install any package you have access to.

Here’s an example package.json for a project that consumes your ESLint config:

  "name": "demo",
  "version": "1.1.0",
  "devDependencies": {
    "@example-group/eslint-config": "^1.1",
    "eslint": "^7.2"
  "scripts": {
    "lint": "eslint ."
  "eslintConfig": {
    "extends": [

Your ESLint package should be added as a development dependency so it’s not installed unnecessarily. Add an eslintConfig block to configure ESLint with your package. You can now run ESLint with your custom configuration using npm run lint.

Using Your Packages In CI Builds

You don’t need to do anything special to use your private packages in a CI pipeline. You’ll need to update your CI script to login to your GitLab npm registry. It’s usually best to generate a project-level access token with the registry scopes, rather than using your own user-level token.

Use the commands shown above to setup npm authentication within your pipeline. Your build should then be able to run npm ci to download your dependencies and run ESLint.

Here’s an example .gitlab-ci.yml file:

  - lint

  stage: lint
  image: node:14
    - npm config set @example-group:registry
    - npm config set -- '//' "${API_TOKEN}"
    - npm ci
    - npm run lint -- --cache
      - .eslintcache
      - node_modules/

The pipeline contains one stage which authenticates to the private registry, installs dependencies and finally invokes ESLint using the lint script. This was setup earlier when creating the project’s package.json file. To get the API_TOKEN value into your pipeline, create an environment variable in GitLab. This lets you securely provide a token without hardcoding it into your file.

If you plan to publish packages from CI, remember that each release needs its own unique version number. You’ll need to devise a strategy to update the version field in your package.json before you npm publish. It’s not possible to overwrite existing package tags.


Combining ESLint’s plugin system with GitLab’s private npm registries lets you share one config with all your team members. You can stop duplicating your ESLint rules across your projects, enhancing convenience and maintainability.

The only friction lies in the one-time npm authentication when a new individual needs to install your package. Consider updating your project’s README to include guidance on logging into your private registry. Authentication will persist until your GitLab API token is deleted.

Profile Photo for James Walker James Walker
James Walker is a contributor to How-To Geek DevOps. He is the founder of Heron Web, a UK-based digital agency providing bespoke software development services to SMEs. He has experience managing complete end-to-end web development workflows, using technologies including Linux, GitLab, Docker, and Kubernetes.
Read Full Bio »