Skip to content
~/sph.sh

Comparing TypeScript Formatting and Linting Tools: Biome, Oxlint, ESLint, and Prettier

A comprehensive comparison of modern TypeScript linting and formatting tools - ESLint, Prettier, Biome, and Oxlint - with performance benchmarks, configuration examples, and migration strategies.

The TypeScript tooling landscape has shifted significantly with Rust-based alternatives entering the scene. Working with both traditional and modern tools across different project sizes has taught me that the "best" choice depends heavily on your specific constraints. This comparison covers ESLint, Prettier, Biome, and Oxlint to help you make an informed decision for your TypeScript projects.

Abstract

This analysis compares modern TypeScript formatting and linting tools using a framework focused on performance, feature coverage, integration capabilities, and migration complexity. The goal is to help development teams make informed decisions based on their specific requirements rather than following trends. Key findings: Rust-based tools offer 15-50x speed improvements with some trade-offs in ecosystem maturity and type-aware rule coverage.

Context: Why This Matters Now

TypeScript developers face several practical challenges with traditional tooling:

CI/CD Pipeline Impact: ESLint + Prettier can take 3-5 seconds to lint a 10k-line monorepo. In organizations running hundreds of builds daily, this adds up.

Configuration Complexity: Traditional setups often require 4+ configuration files - eslint.config.js, .prettierrc, tsconfig.json, and various plugins.

Dependency Overhead: ESLint + Prettier with common plugins can pull in 100+ npm packages, increasing installation time and maintenance burden.

Tool Coordination: Managing ESLint-Prettier conflicts requires careful configuration with eslint-config-prettier or similar solutions.

Type-Aware Linting Cost: typescript-eslint with full type checking can add 10-20 seconds to linting time on larger codebases.

Tool Landscape Overview

The tools fall into three categories: Linters (code quality), Formatters (code style), and All-in-One solutions.

Linters

ESLint: The Established Standard

ESLint has been the JavaScript/TypeScript linting standard since 2013. The current v9.x release defaults to flat config (eslint.config.js), with the legacy .eslintrc format deprecated.

Key Characteristics:

  • Plugin-based architecture with 1000+ available plugins
  • Full TypeScript support via typescript-eslint
  • Mature IDE integration
  • Active development with v10.0.0-rc.0 available

Oxlint: The Speed-Focused Linter

Oxlint is part of the Oxc project, focusing purely on linting with maximum performance. Version 1.0 released June 2025.

Key Characteristics:

  • Linter only (no formatting)
  • 655+ built-in rules
  • Type-aware linting via tsgolint (based on typescript-go)
  • 50-100x faster than ESLint in benchmarks
  • JS plugin support in preview (October 2025)

Formatters

Prettier: The Formatting Pioneer

Prettier established the opinionated formatter category in 2017. Version 3.7 (November 2025) continues the stable evolution with improved TypeScript formatting consistency.

Key Characteristics:

  • Opinionated code formatter only (no linting)
  • 97% formatting compatibility with Biome
  • v3.6 introduced --experimental-cli with significant speed improvements
  • Extensive plugin ecosystem for various languages

dprint: The Pluggable Formatter

dprint is a Rust-based pluggable formatting platform that prioritizes speed and configurability.

Key Characteristics:

  • WebAssembly-based plugin system
  • Highly configurable (unlike Prettier's opinionated approach)
  • Supports TypeScript, JSON, Markdown, TOML, CSS, and more
  • Can use Prettier or Biome plugins for compatibility
  • Configuration sharing via URL imports

oxfmt: The Fastest Formatter (Alpha)

Part of the Oxc ecosystem, oxfmt is a Prettier-compatible formatter currently in alpha.

Key Characteristics:

  • 30x faster than Prettier, 3x faster than Biome
  • Supports JS/TS(X), JSON, YAML, HTML, CSS, GraphQL, Markdown
  • Native Tailwind class sorting
  • Missing: Prettier plugins, some configuration options

All-in-One Solutions

Biome: The Unified Toolchain

Biome emerged in 2023 as a community fork of Rome. Version 2.x (released June 2025) represents the stable, feature-complete release.

Key Characteristics:

  • Single binary combining linter, formatter, and import organizer
  • Written in Rust for performance
  • 420+ built-in rules from ESLint, typescript-eslint, and other sources
  • Type-aware linting via Biotype (covering approximately 75-85% of typescript-eslint rules)
  • Plugin system introduced in v2.0

The Broader Oxc Ecosystem

Oxlint is just one part of the Oxc project. The ecosystem includes several high-performance tools built on a shared Rust foundation:

ToolPurposePerformance
oxc-parserJS/TS(X) parsing3x faster than SWC, passes all Test262 tests
oxfmtCode formatting (alpha)30x faster than Prettier, 3x faster than Biome
oxc-transformTranspilationTypeScript, JSX, ES2015 lowering, React Fast Refresh
oxc-resolverModule resolution28x faster than enhanced-resolve
oxc-minifyCode minificationDead code elimination, mangling

oxfmt is particularly interesting as a Prettier-compatible formatter currently in alpha. It supports JS/TS(X), JSON, YAML, HTML, CSS, GraphQL, and Markdown with native Tailwind class sorting. However, it lacks Prettier plugin support and some configuration options.

For projects already using Oxlint, the ecosystem provides a path toward a fully Rust-based toolchain as these tools mature.

Performance Analysis

Raw Speed Comparison

Based on testing with a 10k-line monorepo, typical results look like this:

ToolApproximate TimeRelative to ESLint
ESLint (no type-aware)3-5sbaseline
ESLint + typescript-eslint10-20s3-5x slower
Prettier2-3ssimilar
Biome~200ms15-25x faster
Oxlint~70ms50-100x faster

Important caveat: Your results will vary based on codebase structure, rule configuration, and hardware. Always benchmark on your actual project.

Type-Aware Linting Performance

Type-aware rules provide valuable checks like detecting floating promises or incorrect type assertions, but they come with a performance cost:

ToolVue Core BenchmarkSentry Benchmark
ESLint + typescript-eslint~21s~55s
Oxlint + tsgolint~2.5s~4.4s
Biome with Biotype~3s~6s

The Rust-based tools show 7-12x improvements for type-aware linting, though with varying rule coverage.

CI/CD Impact

For a team running 100 builds/day on a 10k-line codebase:

ToolLint time per buildMonthly CI minutes
ESLint + Prettier~5s~250 min
Biome~200ms~10 min
Oxlint~70ms~3.5 min

The reduction in CI minutes is meaningful but rarely the primary decision factor. Developer experience improvements from faster local feedback are often more impactful.

Feature Comparison Matrix

Linter Comparison

FeatureESLintOxlintBiome
Built-in rules~300 (core)655+420+
Type-aware rules59 (typescript-eslint)43 (tsgolint)~75-85% (Biotype)
React hooks rulesVia pluginBuilt-inBuilt-in
React Compiler rules[email protected]+NoNo
Accessibility (a11y)eslint-plugin-jsx-a11yBuilt-inBuilt-in
Vue/Svelte/AstroVia pluginsLimitedExperimental (v2.3+)
CSS lintingVia stylelintNoYes
GraphQLVia pluginNoYes
Auto-fixYesLimitedYes
Plugin ecosystem1000+ pluginsJS plugins (preview)Limited (v2.0+)
Speed vs ESLintbaseline50-100x faster15-25x faster

Formatter Comparison

FeaturePrettierBiomedprintoxfmt (alpha)
PhilosophyOpinionatedOpinionatedConfigurablePrettier-compatible
LanguageJavaScriptRustRustRust
TypeScript supportYesYesYesYes
JSX/TSXYesYesYesYes
JSON/YAML/MarkdownYesYesYes (plugins)Yes
CSS/HTMLYesYesYes (Malva plugin)Yes
GraphQLYesYesVia pluginYes
Tailwind sortingVia pluginNoNoNative
Plugin systemYes (extensive)LimitedWebAssemblyNo
ConfigurationMinimalModerateExtensiveMinimal
Speed vs Prettierbaseline~25x faster~25x faster~30x faster
MaturityStableStableStableAlpha

Combined Tool Comparison

FeatureESLint + PrettierBiomeOxlint + oxfmtOxlint + dprint
LintingYesYesYesYes
FormattingYesYesYes (alpha)Yes
Import SortingVia pluginYesNoVia plugin
Config files2-4 files1 file2 files2 files
Dependencies100+ packages1 package2 packages2 packages
Type-awareFull~75-85%43 rules43 rules
MaturityMatureStableMixedStable

Type-Aware Rule Coverage

This deserves special attention since type-aware rules catch subtle bugs that syntax-only analysis misses:

typescript-eslint (ESLint): Full TypeScript type system coverage with 50+ type-aware rules.

Biotype (Biome): Custom type synthesizer covering approximately 75-85% of typescript-eslint rules. First shipped rule: noFloatingPromises. Note that Biome doesn't use the TypeScript compiler, so edge cases may differ.

tsgolint (Oxlint): Based on typescript-go (the foundation for TypeScript v7.0), currently offering 43 type-aware rules. Benefits from ongoing TypeScript team optimizations.

Configuration Examples

Traditional ESLint + Prettier Setup

javascript
// eslint.config.js (ESLint v9 flat config)import eslint from "@eslint/js";import tseslint from "typescript-eslint";import reactPlugin from "eslint-plugin-react";import reactHooksPlugin from "eslint-plugin-react-hooks";import prettierConfig from "eslint-config-prettier";
export default tseslint.config(  eslint.configs.recommended,  ...tseslint.configs.strictTypeChecked,  {    languageOptions: {      parserOptions: {        project: true,        tsconfigRootDir: import.meta.dirname,      },    },  },  reactPlugin.configs.flat.recommended,  reactPlugin.configs.flat["jsx-runtime"],  {    plugins: {      "react-hooks": reactHooksPlugin,    },    rules: reactHooksPlugin.configs.recommended.rules,  },  prettierConfig,);
json
// .prettierrc{  "semi": true,  "singleQuote": true,  "tabWidth": 2,  "trailingComma": "es5"}

Biome Configuration

json
// biome.json{  "$schema": "https://biomejs.dev/schemas/latest/schema.json",  "assist": {    "actions": {      "source": {        "organizeImports": "on"      }    }  },  "linter": {    "enabled": true,    "rules": { "recommended": true }  },  "formatter": {    "enabled": true,    "indentStyle": "space",    "indentWidth": 2  },  "javascript": {    "formatter": {      "quoteStyle": "single",      "semicolons": "always",      "trailingCommas": "es5"    }  }}

Oxlint Configuration

json
// oxlint.json{  "$schema": "./node_modules/oxlint/configuration_schema.json",  "rules": {    "no-unused-vars": "warn",    "react-hooks/rules-of-hooks": "error",    "react-hooks/exhaustive-deps": "warn"  },  "plugins": ["react", "react-hooks", "typescript"]}

dprint Configuration

json
// dprint.json{  "typescript": {    "lineWidth": 100,    "indentWidth": 2,    "useTabs": false,    "semiColons": "always",    "quoteStyle": "preferSingle",    "trailingCommas": "onlyMultiLine"  },  "json": {},  "markdown": {},  "includes": ["**/*.{ts,tsx,js,jsx,json,md}"],  "excludes": ["**/node_modules", "**/dist"]}

The configuration complexity difference is notable: Biome and Oxlint require a single JSON file, while ESLint + Prettier typically involves multiple files and plugin coordination.

React and React Native Considerations

React Support

All tools handle React/JSX well:

FeatureESLintBiomeOxlint
JSX/TSX parsingYesYesYes
React hooks rulesFull (eslint-plugin-react-hooks)90% built-inBuilt-in
React Compiler rulesYes ([email protected]+)NoNo
Accessibility (a11y)eslint-plugin-jsx-a11yBuilt-inBuilt-in
React refresh rulesYesYesYes

React Native Specific Configuration

Biome for React Native:

json
// biome.json{  "files": {    "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]  },  "javascript": {    "parser": {      "unsafeParameterDecoratorsEnabled": true    },    "globals": ["__DEV__", "fetch", "FormData"]  },  "linter": {    "rules": {      "style": {        "noNonNullAssertion": "warn"      },      "correctness": {        "noUnusedVariables": "warn",        "useHookAtTopLevel": "error"      }    }  }}

ESLint with Expo SDK 53+:

javascript
// eslint.config.jsconst expoConfig = require("eslint-config-expo/flat");const { defineConfig } = require("eslint/config");
module.exports = defineConfig([  expoConfig,  {    rules: {      "react-native/no-unused-styles": "error",      "react-native/split-platform-components": "warn",      "react-native/no-inline-styles": "warn",    },  },]);

Recommendation for React Native projects:

  • New projects: Consider Biome for simplicity
  • Expo projects: Use eslint-config-expo for comprehensive support
  • Speed-critical CI: Add Oxlint for fast initial feedback
  • Type safety priority: Keep typescript-eslint for full coverage

IDE Integration

VS Code

Biome VS Code Extension (v3):

json
// .vscode/settings.json{  "editor.formatOnSave": true,  "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"  }}

Features include multi-root workspace support, single-file mode for untitled files, and automatic version reloading.

IDE Support Summary

ToolVS CodeWebStorm/IntelliJNeovim
ESLintOfficial extensionBuilt-inBuilt-in LSP
PrettierOfficial extensionBuilt-inVia plugin
BiomeOfficial extension (v3)Official pluginbiome-lsp
OxlintCommunity extensionNo official supportCLI only

WebStorm/IntelliJ users should note that Biome integration is less mature than VS Code - there's no manual "Format with Biome" action, only format-on-save.

Monorepo Configuration

Turborepo + Biome

json
// turbo.json{  "tasks": {    "lint": {      "dependsOn": ["^build"],      "outputs": [],      "cache": true    },    "format": {      "outputs": [],      "cache": false    }  }}
json
// Root biome.json{  "extends": ["./packages/config-biome/base.json"],  "files": {    "ignore": ["**/dist/**", "**/node_modules/**"]  }}

Turborepo + ESLint (Shared Config)

javascript
// packages/eslint-config/base.jsconst tseslint = require("typescript-eslint");
module.exports = tseslint.config(  ...tseslint.configs.recommended,  {    rules: {      "@typescript-eslint/no-explicit-any": "error",    },  });
// apps/web/eslint.config.jsconst baseConfig = require("@repo/eslint-config/base");const nextConfig = require("@repo/eslint-config/next");
module.exports = [...baseConfig, ...nextConfig];

All tools integrate reasonably well with Turborepo, pnpm workspaces, and Nx. The main difference is configuration complexity, not fundamental compatibility.

CI/CD Integration

GitHub Actions with Biome

yaml
# .github/workflows/lint.ymlname: Linton: [push, pull_request]
jobs:  lint:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v4      - uses: biomejs/setup-biome@v2        with:          version: latest      - run: biome ci .        # biome ci provides GitHub annotations automatically

Parallel Linting Strategy (Oxlint + ESLint)

yaml
# Use oxlint for fast feedback, ESLint for type-aware rulesjobs:  fast-lint:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v4      - uses: oxc-project/setup-oxc@v1      - run: oxlint --deny-warnings .  # Completes in <1s
  type-aware-lint:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v4      - run: npm ci      - run: npx eslint .  # Type-aware rules

This parallel approach provides fast feedback while maintaining thorough type checking.

Migration Strategies

ESLint + Prettier to Biome

Migration complexity: Low to Medium Time estimate: 1-2 days for small projects, 1 week for larger projects

bash
# Step 1: Install Biomenpm install --save-dev @biomejs/biome
# Step 2: Run automatic migrationnpx biome migrate eslint --writenpx biome migrate prettier --write
# Step 3: Verify migrationnpx biome check . --diagnostic-level=info
# Step 4: Remove old dependenciesnpm uninstall eslint prettier eslint-config-prettier \  eslint-plugin-react eslint-plugin-react-hooks \  @typescript-eslint/eslint-plugin @typescript-eslint/parser
# Step 5: Clean up old config filesrm .eslintrc.* .prettierrc* .eslintignore .prettierignore

Gotchas to watch for:

  • Some ESLint plugins have no Biome equivalent
  • JSON-only config limits dynamic configuration (no JavaScript expressions)
  • Vue/Svelte/Astro support is experimental

Gradual Migration: ESLint + Oxlint

Migration complexity: Low Time estimate: 1-2 hours

bash
# Install eslint-plugin-oxlint to disable overlapping rulesnpm install --save-dev oxlint eslint-plugin-oxlint
javascript
// eslint.config.jsimport oxlint from "eslint-plugin-oxlint";
export default [  ...tseslint.configs.recommended,  oxlint.configs["flat/recommended"],  // Disables overlapping rules];
json
// package.json{  "scripts": {    "lint": "oxlint && eslint ."  // Fast feedback first  }}

This approach provides immediate speed gains with zero risk - you keep your full ESLint configuration while getting faster initial feedback from Oxlint.

Formatter-Only Migration (Prettier to Biome)

Migration complexity: Very Low Time estimate: 30 minutes

bash
# Install Biomenpm install --save-dev @biomejs/biome
# Migrate Prettier confignpx biome migrate prettier --write
# Disable Biome linter if you want to keep ESLint# In biome.json:# "linter": { "enabled": false }

This gives you faster formatting while keeping your familiar linting setup.

Decision Framework

When to Choose Each Tool

Choose ESLint + Prettier when:

  • You need specific ESLint plugins (custom rules, framework-specific plugins)
  • Your team is comfortable with the existing setup
  • You need Vue, Svelte, or Astro support
  • Full typescript-eslint type coverage is required

Choose Biome when:

  • Starting a new project and want simplicity
  • You value reduced configuration and dependency count
  • Your project uses React/React Native without exotic plugins
  • You're willing to accept 75-85% type-aware rule coverage

Choose Oxlint (alongside ESLint) when:

  • CI speed is a priority but you can't fully migrate
  • You want faster local development feedback
  • You're evaluating Rust-based tools without commitment

Choose Oxlint (replacing ESLint) when:

  • You only need linting (use Biome or Prettier for formatting)
  • Maximum speed is the priority
  • Your rule requirements are covered by Oxlint's 655+ built-in rules

Common Pitfalls

Pitfall 1: Assuming 100% Parity

Biome and Oxlint don't cover all ESLint rules. Before migrating, audit which rules you actually use. Many projects enable far more rules than they need.

Pitfall 2: Breaking Formatting in Open PRs

Switching formatters creates massive diffs in open PRs. Format the entire codebase in one PR before switching, and communicate with your team about the timing.

Pitfall 3: IDE Extension Conflicts

Multiple formatters fighting for format-on-save causes confusion. Disable conflicting extensions and use workspace-specific settings.

Pitfall 4: Not Benchmarking Your Codebase

Published benchmarks are useful for comparison but may not reflect your situation. Always benchmark on your actual codebase before making decisions.

Pitfall 5: Ignoring Type-Aware Rule Gaps

If you rely heavily on type-aware rules like @typescript-eslint/no-floating-promises or @typescript-eslint/strict-boolean-expressions, verify coverage before migrating.

Conclusion

The TypeScript linting and formatting landscape has expanded with compelling Rust-based alternatives. Here's the practical summary:

Performance: Biome and Oxlint deliver 15-50x speed improvements over ESLint + Prettier. This translates to faster CI/CD and better developer experience.

Simplicity: Biome's single-tool approach significantly reduces configuration complexity and dependency count.

Trade-offs: Rust-based tools offer less extensive plugin ecosystems and approximately 75-85% coverage for type-aware rules. For many projects, this is sufficient. For others, it's not.

Migration path: The parallel strategy (Oxlint + ESLint) offers the safest path to faster linting without sacrificing features.

Recommendation: For new projects, Biome is worth serious consideration. For existing projects, evaluate the parallel strategy first before committing to a full migration.

The tooling continues to evolve rapidly. Both Biome and Oxlint are actively developed with improving type-aware capabilities. What's optimal today may shift as these tools mature.

Sources

Related Posts