Editors, LSP, and Daemon
Purpose and Scope
This page explains how Biome reaches editors and other long-lived clients. In normal terminal use, developers call the biome executable directly, but editor integrations need a process that can answer repeated formatting, linting, and project-state requests with low startup cost. The official editor documentation describes first-party extensions for VS Code, IntelliJ, and Zed, and third-party integrations for tools such as Helix, Neovim, Vim, Sublime Text, Emacs, Nova, and coc.nvim. The common thread is that these integrations either invoke the Biome command-line binary or communicate with Biome through a language-server or daemon-oriented protocol.
Sources: packages/@biomejs/biome/package.json, packages/@biomejs/backend-jsonrpc/package.json
For repository readers, the most useful boundary is the client boundary. The package @biomejs/biome publishes the user-facing biome binary and declares Biome as a web toolchain for formatting, linting, and more. The @biomejs/backend-jsonrpc package sits one layer higher for JavaScript clients that want a Workspace API over JSON-RPC instead of shelling out for every operation. That split is important for editor authors: one package distributes the executable, while the other exposes a client-side bridge to a daemon-backed workspace.
Sources: packages/@biomejs/biome/package.json, packages/@biomejs/backend-jsonrpc/src/index.ts, packages/@biomejs/backend-jsonrpc/package.json
Relevant Source Files
packages/@biomejs/backend-jsonrpc/src/index.ts— exports the JSON-RPC workspace entry points, includingcreateWorkspace()andcreateWorkspaceWithBinary(command), and performs the initialinitializerequest.packages/@biomejs/js-api/src/index.ts— exposes the WebAssembly-orientedBiomeJavaScript API and itsDistributionselector for bundler, Node.js, and web runtimes.packages/@biomejs/plugin-api/index.js— intentionally throws at runtime to steer users away from the plugin package when they meant to use@biomejs/js-api.packages/@biomejs/backend-jsonrpc/package.json— describes the JSON-RPC backend package as bindings to the Workspace API of the Biome daemon and lists platform CLI packages as optional dependencies.packages/@biomejs/biome/package.json— defines the published@biomejs/biomepackage, thebiomebinary entry, supported package keywords, engine requirement, and optional platform CLI dependencies.packages/@biomejs/cli-darwin-arm64/package.json— shows the shape of a platform-specific binary package, constrained todarwinandarm64.
Integration Model
The official editor pages describe editor-facing capabilities in user terms: format on save, format on command, lint files, and apply code fixes. Those tasks map to a small number of integration modes. A first-party extension can use the installed Biome binary and editor APIs directly. A third-party LSP client can start biome lsp-proxy over standard input and output, as the Helix example does. A custom JavaScript integration can use @biomejs/backend-jsonrpc to create a Workspace client that speaks JSON-RPC to a daemon process.
Sources: packages/@biomejs/backend-jsonrpc/src/index.ts, packages/@biomejs/backend-jsonrpc/package.json
The JSON-RPC backend package is explicit about its role: its package description is “Bindings to the JSON-RPC Workspace API of the Biome daemon.” Its source entry point exports createWorkspace(), which discovers the Biome command through getCommand(), returns null when the platform is unsupported, and otherwise delegates to createWorkspaceWithBinary(command). That second entry point accepts a concrete path to a Biome binary distribution, creates a socket, wraps it in a Transport, sends an initialize request, and returns a Workspace wrapper around that transport.
Sources: packages/@biomejs/backend-jsonrpc/src/index.ts, packages/@biomejs/backend-jsonrpc/package.json
This design gives editor and tooling authors two useful choices. If they want Biome to locate the bundled binary from package metadata and optional dependencies, they can call createWorkspace() and handle the null unsupported-platform case. If they already know which executable should be used, they can call createWorkspaceWithBinary(command) and pass the command path explicitly. In both cases, the public contract is a Workspace client rather than a raw socket, so clients can be written around workspace operations while the backend package owns transport setup.
Sources: packages/@biomejs/backend-jsonrpc/src/index.ts
API Components and Runtime Packages
The primary CLI distribution package is @biomejs/biome. It declares a bin entry named biome pointing at bin/biome, which is the command used by editor configurations and terminal examples. The package also lists optional dependencies for platform-specific CLI builds, including Windows, macOS, Linux, ARM, x64, and musl variants. The separate @biomejs/cli-darwin-arm64 package demonstrates that these binary packages are selected by Node package-manager platform constraints such as os: ["darwin"] and cpu: ["arm64"].
Sources: packages/@biomejs/biome/package.json, packages/@biomejs/cli-darwin-arm64/package.json
| Component | Public surface | Role in editor or daemon workflows |
|---|---|---|
@biomejs/biome | bin.biome = bin/biome | Installs the biome executable used by commands such as editor LSP proxy startup and manual CLI invocations. |
@biomejs/backend-jsonrpc | createWorkspace() | Creates a Workspace client using the discovered Biome command, or returns null if unsupported. |
@biomejs/backend-jsonrpc | createWorkspaceWithBinary(command: string) | Creates a Workspace client using a caller-provided Biome binary path. |
@biomejs/js-api | Biome.create({ distribution }) | Loads a WebAssembly-backed API for bundler, Node.js, or web environments. |
@biomejs/plugin-api | runtime guard | Throws an error directing users to @biomejs/js-api when they import the wrong package. |
The JavaScript API is related but not the same as the daemon backend. packages/@biomejs/js-api/src/index.ts exports shared types for configuration and diagnostics from WebAssembly packages, then defines Distribution.BUNDLER, Distribution.NODE, and Distribution.WEB. Biome.create({ distribution }) dynamically imports the matching WebAssembly implementation and constructs a Biome instance. Use this API when a JavaScript program wants embedded WebAssembly behavior; use the JSON-RPC backend when the program wants to communicate with a Biome daemon workspace through the binary.
Sources: packages/@biomejs/js-api/src/index.ts, packages/@biomejs/backend-jsonrpc/src/index.ts
The plugin API package is deliberately not a general-purpose integration surface. Its root index.js immediately throws an error saying the package is intended for Biome JS plugins and asks whether the caller meant @biomejs/js-api. This is a useful troubleshooting signal because a failed import from @biomejs/plugin-api is not a daemon failure, LSP failure, or editor bug. It is package selection feedback: general JavaScript clients should choose @biomejs/js-api, while daemon workspace clients should choose @biomejs/backend-jsonrpc.
Sources: packages/@biomejs/plugin-api/index.js, packages/@biomejs/js-api/src/index.ts, packages/@biomejs/backend-jsonrpc/package.json
Editor and Daemon Execution Flow
A practical editor startup flow begins with binary resolution. The editor extension or LSP client needs a biome executable, commonly from the project’s package installation or from a global installation. The official Helix configuration uses command = "biome" and args = ["lsp-proxy"], which means Helix starts Biome as an LSP server over stdin and stdout. Project-specific behavior depends on root detection; the official example adds biome.json, biome.jsonc, and package.json to the editor’s roots so the editor can start Biome in the intended workspace context.
Sources: packages/@biomejs/biome/package.json
[language-server.biome]
command = "biome"
args = ["lsp-proxy"]
[[language]]
name = "typescript"
roots = ["biome.json", "biome.jsonc", "package.json"]
language-servers = [{ name = "typescript-language-server", except-features = ["format"] }, "biome"]
auto-format = trueWhen the JSON-RPC backend is used instead of a generic LSP client, the startup sequence is visible in source. createWorkspaceWithBinary(command) calls createSocket(command), creates a Transport, sends an initialize JSON-RPC request with empty capabilities and client information for @biomejs/backend-jsonrpc, and only then wraps the transport as a Workspace. That sequence matters because clients should treat initialization as part of connection setup. A Workspace returned from the function has already completed the initial protocol handshake from the package’s perspective.
Sources: packages/@biomejs/backend-jsonrpc/src/index.ts
The official daemon reference names JSON-RPC methods such as biome/open_project, biome/update_settings, biome/file_features, and biome/scan_project. Those method names describe the server-side operations that editor integrations care about: opening a project, pushing configuration, asking which features are supported for a file, and scanning project state. The supplied repository source for this page shows the JavaScript client entry point rather than the server method implementations, so clients should read the daemon reference together with the backend package API: the reference defines method payloads, while the package provides a typed client route into the daemon workspace.
Sources: packages/@biomejs/backend-jsonrpc/src/index.ts, packages/@biomejs/backend-jsonrpc/package.json
Troubleshooting Signals
Most editor problems can be narrowed down by asking which layer failed. If the editor cannot start Biome at all, verify that the biome command resolves to the @biomejs/biome package’s binary and that the correct platform package was installed. The main package depends optionally on platform-specific CLI packages, while each platform package advertises its own os and cpu constraints. A macOS ARM machine, for example, matches the @biomejs/cli-darwin-arm64 package shape shown in the repository evidence.
Sources: packages/@biomejs/biome/package.json, packages/@biomejs/cli-darwin-arm64/package.json
If a JavaScript integration fails before it can make workspace requests, check which package it imported. @biomejs/backend-jsonrpc is the package whose description and source entry point match daemon Workspace access. @biomejs/js-api is the WebAssembly API that chooses between bundler, Node.js, and web distributions. @biomejs/plugin-api is not a substitute for either; it throws immediately with a message pointing users toward @biomejs/js-api. That distinction prevents a common class of integration bugs where the correct package name is close but the runtime contract is completely different.
Sources: packages/@biomejs/backend-jsonrpc/package.json, packages/@biomejs/js-api/src/index.ts, packages/@biomejs/plugin-api/index.js
If Biome starts but editor behavior differs from the terminal, focus on workspace and configuration context. The official Helix guidance demonstrates this by adding Biome configuration files to the editor’s root detection list. That ensures project-specific settings are discoverable before formatting, linting, or feature checks run. For daemon clients, the same idea appears as explicit project and settings operations in the daemon reference: a client opens a project, updates settings, scans the project when needed, and asks which features apply to a file.
Sources: packages/@biomejs/backend-jsonrpc/src/index.ts, packages/@biomejs/backend-jsonrpc/package.json
Next Steps
Use this page when you are choosing an integration path. Choose the CLI binary and lsp-proxy for editors that already speak the Language Server Protocol over stdio. Choose @biomejs/backend-jsonrpc when a JavaScript client needs a daemon Workspace bridge and can manage a long-lived connection. Choose @biomejs/js-api when embedding the WebAssembly implementation is the better fit. After that, read the CLI reference for command behavior, the configuration page for project settings, and the diagnostics page for interpreting the messages returned through editor and daemon workflows.