zed_extension_api

The Rust crate that defines the trait, WASM bindings, and lifecycle contract for building extensions inside the Zed code editor.

Framework
Cargo
v0.7.0
89,443stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
94/100Excellent
Development Activity100
Maintenance100
Community76
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
84/100Excellent
Architecture92
Code Quality78
Innovation85
Learning Curve80

zed_extension_api is the official Rust crate for authoring extensions for the Zed code editor. Rather than exposing a loose grab-bag of helper functions, it hands extension authors a single Extension trait with well-defined lifecycle hooks — language server command resolution, initialization/workspace configuration, completion and symbol labeling, slash commands, and debug adapter configuration — that Zed’s extension host calls into at the right moments. Extensions compile to a cdylib targeting wasm32-wasip2, register themselves with the register_extension! macro, and are loaded by Zed as sandboxed WebAssembly components.

Underneath the ergonomic Rust surface, the crate is generated largely from WIT (WebAssembly Interface Type) definitions versioned per Zed release (wit/since_v0.0.1 through wit/since_v0.8.0), guaranteeing that an extension built against a given API version keeps working against the Zed releases that shipped with it. Supporting modules cover the sandboxed capabilities an extension is allowed: an http_client module with a builder-pattern HttpRequest, a process module for running external commands, and a settings module for reading editor/worktree configuration. Re-exported helpers add GitHub release lookups, npm package installation and version checks, and platform detection — the common building blocks language-server and DAP extensions need without extensions having to shell out or hand-roll their own HTTP stack.

The crate is deliberately unpublished on crates.io for its in-development 0.8.0 version (publish = false in Cargo.toml) while 0.7.0 remains the latest version extension authors actually depend on, reflecting how tightly its releases are coupled to Zed’s own release cadence rather than an independent versioning story.

What You Get

  • An Extension trait with typed lifecycle hooks for language server commands, initialization options, workspace configuration, and completion/symbol labeling
  • A register_extension! macro that wires an extension struct into Zed’s WASM component host with no boilerplate glue code
  • A builder-pattern HttpRequest/http_client module for making sandboxed network requests from within an extension
  • A process module for constructing and running external commands with args, env vars, and captured output
  • A settings module for reading Zed and worktree-scoped configuration values
  • Re-exported helpers for GitHub release lookups, npm package installation/version checks, and current-platform detection
  • Versioned WIT interface definitions (wit/since_v0.0.1 through wit/since_v0.8.0) that pin each extension API surface to the Zed releases it’s compatible with
  • Support for newer extension types beyond language servers: debug adapter protocol (DAP) configuration, slash commands, and MCP context servers

Common Use Cases

  • Building a language server extension that tells Zed how to locate/launch an LSP binary and what initialization options to send it
  • Packaging a syntax highlighter or grammar as a Zed extension via Tree-sitter integration and completion/symbol labeling
  • Writing a debug adapter extension that defines launch/attach configurations for a given debugger via the dap module
  • Building a slash-command extension that runs custom logic inside Zed’s AI assistant panel and returns structured output
  • Fetching a GitHub release asset for a language server binary at install time using latest_github_release/github_release_by_tag_name instead of hand-rolling GitHub API calls
  • Installing and version-checking an npm-distributed language server dependency via the nodejs module before spawning it as a subprocess

Under The Hood

Architecture The crate is a thin, purpose-built Rust façade over machine-generated WIT (WebAssembly Interface Type) bindings: the bulk of extension_api.rs re-exports selected symbols from an internal wit module built by wit-bindgen against interface files under wit/since_v*/, one directory per Zed extension-API version, so a compiled extension stays pinned to the exact capability surface of the Zed release it targets. The public-facing contract is a single Extension trait (Send + Sync) with default no-op implementations for every optional hook — language server command/config resolution, completion and symbol labeling, slash-command execution, DAP configuration — so an extension only overrides what it needs, and the register_extension! macro binds the concrete struct to the WASM component’s Guest implementation that Zed’s extension host calls into. Supporting capability modules (http_client, process, settings) are hand-written ergonomic wrappers (builder patterns, typed errors) around the raw generated bindings, which is what breaks if the WIT-generated wit module’s shape changes between versions.

Tech Stack Pure Rust, built with wit-bindgen 0.41 for WASM Component Model bindings, serde/serde_json for JSON payloads passed across the extension/host boundary, and a build.rs that encodes the crate’s semver into a byte-packed version stamp compiled into every extension binary. Extensions built against this crate target wasm32-wasip2 and compile as cdylib, loaded by Zed’s Rust host process as sandboxed WASI Preview 2 components — there is no runtime dependency on a language server SDK or plugin framework outside the WASM Component Model itself.

Code Quality As a codegen-backed bindings crate, there are no unit test files directly in crates/extension_api/src — correctness is enforced structurally (typed WIT interfaces, Result<T, String> error returns throughout, default trait methods that fail loudly with a descriptive error string if a hook is called but unimplemented) and validated by the much larger Zed monorepo’s integration and extension-host test suites rather than crate-local tests. Naming is consistent and the public surface is deliberately narrow — internal re-exports are marked #[doc(hidden)] to keep the extension-author-facing API small and documented, and the crate carries workspace-wide lint configuration ([lints] workspace = true).

What Makes It Unique Unlike most plugin/extension APIs that hand authors an ambient set of host callbacks, this crate versions its entire capability surface per Zed release via separate WIT directories, so an extension compiled against zed_extension_api = "0.6.0" is contractually guaranteed to keep working against the Zed versions documented in its own compatibility table — a discipline uncommon even among mature plugin ecosystems. Combining that with the WASM Component Model (rather than a scripting language or dynamic library ABI) gives extensions memory-safe sandboxing and cross-platform portability without Zed having to embed a scripting runtime.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search