blog.dopana

Back

In the modern JavaScript/TypeScript development world, Biome and Oxc are two prominent Rust tools that deliver high performance for linting and formatting. Both are written in Rust but have different design philosophies and architectures. This article will dive deep into each tool’s architecture and compare them.

What is Biome?#

Biome is a comprehensive toolchain for web projects, providing formatter and linter usable via CLI and LSP. It’s designed to replace Prettier and ESLint with higher performance in a single integrated package.

Biome Architecture#

Biome uses a server-client architecture with a daemon running in the background:

graph TB
    subgraph "Biome Architecture"
        CLI[CLI Interface]
        LSP[LSP Server]
        Daemon[Daemon Process]
        Workspace[WorkspaceServer]
        Handlers[Language Handlers]
        Foundation[Foundation Layer]
    end
    
    CLI --> Daemon
    LSP --> Daemon
    Daemon --> Workspace
    Workspace --> Handlers
    Handlers --> Foundation

Architecture Layers#

  1. Foundation Layer (biome_rowan, biome_parser, biome_formatter, biome_analyze)

    • Provides reusable primitives
    • Rowan-based parsing infrastructure
    • Common formatting IR
  2. Language Layer (per-language crates)

    • biome_js_parser, biome_js_formatter, biome_js_analyze
    • biome_json_parser, biome_json_formatter, biome_json_analyze
    • Similar for CSS, HTML, GraphQL
  3. Service Layer (biome_service)

    • Orchestrates operations via WorkspaceServer
    • Manages state and caching
  4. Interface Layer (biome_cli, biome_lsp, biome_wasm)

    • Provides multiple consumption methods

Biome’s Formatter#

Biome uses a two-stage formatting system:

graph LR
    CST[CST from Parser] --> Lowering[Lowering Stage]
    Lowering --> IR[FormatElement IR]
    IR --> Printer[Printing Stage]
    Printer --> Output[Formatted Text]
  • Lowering Stage: Converts CST to language-agnostic IR (FormatElement)
  • Printing Stage: Printer consumes IR and decides layout, line breaks based on options

What is Oxc?#

Oxc (The Oxidation Compiler) is a collection of high-performance JavaScript/TypeScript tools written in Rust. It’s designed as a modular, composable set of compiler components that can be used independently or together to build complete toolchains.

Oxc Architecture#

Oxc uses a layered design with modular components:

graph TB
    subgraph "Oxc Architecture"
        Apps[Applications]
        Core[Core Libraries]
        Foundation[Foundation Libraries]
    end
    
    Apps --> Core
    Core --> Foundation
    
    Apps --> oxlint[oxlint]
    Apps --> LSP[Language Server]
    Apps --> NAPI[NAPI Bindings]
    
    Core --> Parser[Parser]
    Core --> Semantic[Semantic]
    Core --> Linter[Linter]
    Core --> Transformer[Transformer]
    Core --> Minifier[Minifier]
    Core --> Codegen[Codegen]
    
    Foundation --> AST[AST]
    Foundation --> Allocator[Allocator]
    Foundation --> Diagnostics[Diagnostics]
    Foundation --> Span[Span]
    Foundation --> Syntax[Syntax]

Architecture Principles#

  1. Zero-Copy: Uses arena allocator (oxc_allocator) for zero-copy operations
  2. Visitor Pattern: AST traversal with automatic visitor generation
  3. Shared Foundation: Common error reporting, source positions, syntax definitions

Oxc’s Linter#

Oxlint uses parallel execution architecture with dual branch strategy:

graph TB
    LintService[LintService] --> Runtime[Runtime]
    Runtime --> process_source[process_source]
    process_source --> Parser[Parser]
    Parser --> AST[AST]
    AST --> LintContext[LintContext]
    LintContext --> Linter[Linter]
    Linter --> execute_rules[execute_rules]
    
    execute_rules --> strategy1[Strategy 1: nodes → rules]
    execute_rules --> strategy2[Strategy 2: rules → nodes]
    
    strategy1 --> RULE_BUCKETS[RULE_BUCKETS]
    strategy2 --> RULE_BUCKETS
  • Dual Branch Strategy: Switches between (nodes → rules) or (rules → nodes) based on file size
  • Parallel Execution: Uses rayon for parallel file processing
  • Type-Aware Linting: Supports type-aware linting

Oxc’s Formatter (oxfmt)#

Oxc formatter uses a three-stage formatting system:

graph LR
    AST[AST from Parser] --> IRGen[IR Generation]
    IRGen --> IR[FormatElement IR]
    IR --> Transform[IR Transformation]
    Transform --> sort_imports[sort_imports]
    sort_imports --> Printer[Printing]
    Printer --> Output[Formatted Text]
  • IR Generation: AST traversal using Format trait
  • IR Transformation: Applies transformations like sort_imports
  • Printing: Printer consumes IR and renders final output

[!NOTE] Oxc formatter core is ported from Biome’s biome_formatter crate, but with completely language-agnostic design.

Detailed Comparison#

1. Design Philosophy#

FeatureBiomeOxc
GoalAll-in-one toolchain replacing Prettier + ESLintModular components usable independently
Multi-languageSupports JS/TS, JSON, CSS, HTML, GraphQLFocused on JS/TS with expansion plans
DeploymentDaemon process for IDE integrationTraditional CLI with NAPI bindings

2. Memory Management#

FeatureBiomeOxc
AllocatorRowan-based infrastructureArena allocator (oxc_allocator)
Zero-CopyYes, but less explicitYes, as core design principle
AST StorageRowan syntax treeArena-based AST nodes

3. Performance Optimization#

FeatureBiomeOxc
Parallel ExecutionYes, via WorkspaceServerYes, using rayon
CachingDaemon process cacheRuntime cache in LintService
Rule ExecutionSequential executionDual branch strategy based on file size

4. Extensibility#

FeatureBiomeOxc
Plugin SystemSupports pluginsJS plugins (alpha) for ESLint compatibility
Language SupportExtensionHandler patternModule crates per language
Custom RulesVia biome configurationVia rule configuration

5. Formatting Architecture#

FeatureBiomeOxc
Stages2 stages (Lowering + Printing)3 stages (IR Gen + Transform + Printing)
IR DesignFormatElement in biome_formatterFormatElement in oxc_formatter_core
Comment HandlingIntegrated in formatterCursor-based comment system
Prettier CompatibilityYes, with testingYes, as design goal

When to Use Which?#

Choose Biome when#

  • You want an all-in-one solution replacing both Prettier and ESLint
  • Need multi-language support (CSS, HTML, GraphQL)
  • Want daemon process for better IDE integration
  • Team wants unified toolchain with simple configuration

Choose Oxc when#

  • You want modular components that can be used independently
  • Need maximum performance for CI/CD environments
  • Want type-aware linting with TypeScript semantics
  • Building custom toolchain from components

Usage Examples#

Biome#

# Install
npm install -D @biomejs/biome

# Format and lint a file
biome check src/file.ts

# Format and lint entire project
biome check .

# Watch mode for development
biome check --watch .
bash

Oxc#

# Install oxlint
npm install -D oxlint

# Lint a file
oxlint src/file.ts

# Lint entire project with parallel execution
oxlint src/

# Type-aware linting
oxlint --tsconfig tsconfig.json src/
bash

Conclusion#

Both Biome and Oxc represent the future of JavaScript/TypeScript tooling with high Rust performance. Biome excels at all-in-one experience with multi-language support, while Oxc shines at modular design and maximum performance.

The choice between the two tools depends on your project’s specific needs: unified toolchain vs flexible components, multi-language support vs JS/TS focus, IDE integration vs CI performance.

References#