Muhammad Tehseen Khan

Software Engineer

Simplify Your Toolchain with Biome - A Modern Alternative to ESLint and Prettier

If you've been developing JavaScript or TypeScript applications for a while, you're probably familiar with the classic combo: ESLint for linting and Prettier for formatting. While these tools have served us well, they come with some pain points - slow performance, complex configuration, and occasional conflicts between the two.

Enter Biome: a modern, unified toolchain that aims to replace both ESLint and Prettier with a single, blazing-fast tool built on Rust. In this guide, I'll show you how to set up Biome in your Next.js project and say goodbye to the ESLint + Prettier combo.

Why Switch to Biome?

Before we dive into the setup, let's talk about why you might want to make the switch:

  • Speed: Biome is significantly faster - up to 15x faster than ESLint without plugins and about 4x faster even when ESLint is running single-threaded.
  • Simplicity: One tool instead of two means one configuration file and fewer dependencies.
  • Consistency: No more conflicts between your linter and formatter.
  • Modern defaults: Biome comes with sensible defaults for modern JavaScript and TypeScript projects.
  • Active development: Biome is actively maintained and sponsored by Vercel.

I recently switched to Biome in a few of my projects, and the difference in performance is immediately noticeable, especially in larger codebases.

Installation

Let's start by installing Biome:

terminal
npm install --save-dev --save-exact @biomejs/biome

I recommend using the --save-exact flag to pin the exact version of Biome. This ensures that everyone on your team uses the same version, preventing subtle formatting differences between developers.

Configuration

Next, let's create a Biome configuration file. You can generate a basic configuration file by running:

terminal
npx @biomejs/biome init

This will create a biome.json file in your project root with default settings. Let's take a look at what it generates:

biome.json
{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "vcs": {
    "enabled": false,
    "clientKind": "git",
    "useIgnoreFile": false
  },
  "files": {
    "ignoreUnknown": false,
    "ignore": []
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "tab"
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double"
    }
  }
}

This is a good starting point, but let's customize it to better match common Next.js project preferences:

biome.json
{
  "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
  "vcs": {
    "enabled": false,
    "clientKind": "git",
    "useIgnoreFile": false
  },
  "files": {
    "ignoreUnknown": false,
    "ignore": []
  },
  "linter": {
    "ignore": ["node_modules", "dist", "build", "public", ".next", ".velite"],
    "enabled": true,
    "rules": {
      "recommended": true,
      "a11y": {
        "noSvgWithoutTitle": "off",
        "useKeyWithClickEvents": "off",
        "useSemanticElements": "off",
        "useKeyWithMouseEvents": "warn"
      },
      "suspicious": {
        "noExplicitAny": "warn",
        "noConsoleLog": "warn",
        "noArrayIndexKey": "warn"
      },
      "style": {
        "useConst": "off",
        "useTemplate": "off",
        "noParameterAssign": "warn",
        "noNonNullAssertion": "off",
        "useExponentiationOperator": "off",
        "noUnusedTemplateLiteral": "warn"
      },
      "correctness": {
        "useExhaustiveDependencies": "off"
      },
      "complexity": {
        "noExtraBooleanCast": "off"
      },
      "security": {
        "noDangerouslySetInnerHtml": "off"
      }
    }
  },
  "formatter": {
    "ignore": ["node_modules", ".next", ".velite"],
    "enabled": true,
    "indentWidth": 2,
    "indentStyle": "space",
    "lineWidth": 80
  },
  "javascript": {
    "formatter": {
      "trailingCommas": "es5",
      "semicolons": "always",
      "quoteStyle": "single"
    }
  },
  "organizeImports": {
    "enabled": true
  }
}

Let's break down some of the key changes:

  1. VCS Integration: Enabled Git integration and respect for .gitignore files.
  2. File Ignores: Added common directories to ignore.
  3. Formatter Settings: Changed to spaces instead of tabs (2 spaces is common in Next.js projects).
  4. JavaScript Formatter: Set single quotes and trailing commas to match common React/Next.js conventions.
  5. Linter Rules: Added some additional rules that are helpful for React/Next.js development.

Adding Scripts to package.json

Now, let's add some scripts to our package.json to make it easy to run Biome:

package.json
{
  "scripts": {
    // ... your existing scripts
    "format": "biome format --write .",
    "lint": "biome lint .",
    "check": "biome check --apply ."
  }
}

These scripts give you three options:

  • npm run format: Just format your code
  • npm run lint: Just lint your code
  • npm run check: Do both formatting and linting, and apply safe fixes

Migrating from ESLint and Prettier

If you're migrating from ESLint and Prettier, Biome provides helpful commands to convert your existing configurations:

terminal
# Migrate from ESLint
npx @biomejs/biome migrate eslint --write

# Migrate from Prettier
npx @biomejs/biome migrate prettier --write

These commands will read your existing .eslintrc and .prettierrc files and convert them to Biome's configuration format. It's not perfect, but it gives you a great starting point.

Setting Up Editor Integration

To get the most out of Biome, you'll want to set up editor integration. Biome has official extensions for VS Code and other editors.

For VS Code, install the Biome extension and add this to your settings.json:

.vscode/settings.json
{
  "editor.defaultFormatter": "biomejs.biome",
  "[javascript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  },
  "editor.formatOnSave": true,
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.preferences.autoImportFileExcludePatterns": [
    "next/router.d.ts",
    "next/dist/client/router.d.ts"
  ],
  "terminal.integrated.localEchoStyle": "dim",
  "search.exclude": {
    "**/node_modules": true
  }
}

Setting Up Pre-commit Hooks with Husky

If you're already using Husky for pre-commit hooks (as discussed in my previous post), you can easily integrate Biome:

.lintstagedrc.mjs
export default {
  // Run type-checking on TypeScript files
  "**/*.{ts,tsx}": "tsc-files --noEmit --skipLibCheck",
  
  // Use Biome for linting and formatting
  "**/*.{js,jsx,ts,tsx,json,css,md}": "biome check --apply"
};

Comparing Biome with ESLint + Prettier

After using Biome for a few weeks, here's what I've noticed:

Pros:

  • Speed: Biome is noticeably faster, especially on larger projects.
  • Simplicity: One tool, one config file, fewer dependencies.
  • Consistency: No more fighting between linter and formatter.
  • Modern defaults: The default rules are well-suited for modern JavaScript/TypeScript projects.

Cons:

  • Fewer rules: Biome doesn't have as many rules as ESLint with all its plugins.
  • Less mature: Being newer, it doesn't have the same ecosystem as ESLint.
  • Plugin system: Biome's plugin system is still in beta.

For most projects, especially Next.js applications, the pros far outweigh the cons. The speed improvement alone makes it worth considering.

Real-world Performance

On one of my larger Next.js projects with about 200 files, here's what I observed:

  • ESLint + Prettier: ~8 seconds to lint and format all files
  • Biome: ~1.2 seconds to do the same

That's a significant improvement, especially when you're running these tools frequently during development.

Conclusion

Biome represents a modern approach to JavaScript/TypeScript tooling - faster, simpler, and more integrated. While it might not have all the features of the ESLint ecosystem yet, it's rapidly evolving and already provides a great developer experience for most projects.

If you're starting a new project or looking to simplify your toolchain, give Biome a try. The speed improvements and reduced configuration complexity make it a compelling alternative to the traditional ESLint + Prettier combo.

Have you tried Biome? Let me know your experiences in the comments!

Happy coding!