How Zed's Theme System Supports Custom Themes and Syntax Highlighting
Zed's theme system utilizes a JSON-driven schema to define UI palettes and syntax highlighting rules, enabling custom themes through the ThemeRegistry and runtime merging of user overrides.
Zed's theme system provides a flexible, JSON-based architecture for customizing UI colors and syntax highlighting in the zed-industries/zed editor. Built around the crates/theme crate, it supports both user-defined themes and extension-bundled themes through a registry-based loading mechanism that validates against the official schema at https://zed.dev/schema/themes/v0.2.0.json.
Core Architecture of Zed's Theme System
The theming infrastructure is organized into three distinct layers within the crates/theme directory, separating data models, loading logic, and syntax highlighting definitions.
Theme Loading and Registry
The ThemeRegistry struct in crates/theme/src/registry.rs (lines 22–58) serves as the central authority for theme discovery and storage. On initialization, the registry walks the user theme directory and extension folders, parsing each JSON file into a ThemeMeta struct stored in a HashMap<String, ThemeMeta>. Each entry contains a ThemeFamily grouping light and dark variants, along with a lazily-computed Theme instance that is materialized only when selected.
Theme Data Model
The Theme struct defined in crates/theme/src/theme.rs (lines 336–374) encapsulates all visual styling data through a ThemeStyles object. This structure includes:
colors: ThemeColors– UI element colors for backgrounds, borders, and accentssyntax: Arc<SyntaxTheme>– The syntax highlighting definition shared across editor instances- Status bar, title bar, and accent style groups – Context-specific UI styling
Syntax Highlighting Engine
Syntax coloring is governed by the SyntaxTheme type in crates/theme/src/styles/syntax.rs. This file defines the HighlightStyle struct and the merging logic that combines built-in defaults with user customizations. The SyntaxTheme::merge method (lines 60–68) takes a vector of (String, HighlightStyle) pairs and produces a new theme instance where custom definitions override base values. At runtime, the editor queries specific token colors via Theme::syntax_color(name) (lines 386–387 in theme.rs), which delegates to the underlying SyntaxTheme.
Creating and Loading Custom Themes
Zed supports custom themes through JSON configuration files placed in specific user directories, automatically registering them on startup without requiring a restart.
JSON Schema and File Structure
Custom themes must conform to version v0.2.0 of the Zed theme schema. A valid theme JSON file defines a top-level object containing:
nameandtype(light or dark) – Metadata for the theme selectorcolors– A map of UI color keys to hex valuessyntax– A map of token types toHighlightStyleobjects defining color, italic, bold, and underline properties
User Theme Directories
Zed scans for theme files in platform-specific configuration directories. On Linux and macOS, place .json theme files in ~/.config/zed/themes. On Windows, use %APPDATA%\Zed\themes. The ThemeRegistry::load_all() method recursively searches these paths during initialization, validating each file against the schema before adding it to the registry's internal HashMap.
Runtime Registration
Once loaded, themes appear immediately in the Theme Selector UI. The registry resolves light and dark family groupings, allowing users to switch between variants without restarting the editor. This hot-loading capability is implemented through the lazy evaluation of Theme instances from their ThemeMeta descriptors stored in the registry.
Merging Theme Overrides and Extensions
Beyond static JSON files, Zed supports dynamic theme customization through settings overrides and extension bundles, enabling granular control without modifying core theme files.
User Settings Overrides
Users can apply targeted syntax highlighting modifications through the theme_overrides setting in settings.json. The ThemeSettings::apply_overrides function in crates/theme/src/settings.rs (lines 571–573) processes these overrides by calling SyntaxTheme::merge with the user-provided (token, HighlightStyle) pairs. This produces a new Arc<SyntaxTheme> that supersedes the base theme's defaults while preserving unmodified token styles.
Extension-Based Themes
Extensions can bundle themes by including JSON files in an extensions/<name>/themes/ directory. The theme_extension::ThemeRegistryProxy struct in crates/theme_extension/src/theme_extension.rs (line 21) provides the register_theme_from_path method, which extensions call during initialization to register bundled themes at runtime. This proxy interface makes extension themes immediately available in the Theme Selector without requiring a Zed restart, effectively hot-swapping the theme registry's contents.
Key Files Reference
The following source files implement Zed's theming infrastructure:
| File | Role | Location |
|---|---|---|
crates/theme/src/theme.rs |
Core Theme and ThemeFamily structs, UI-style accessors |
crates/theme/src/theme.rs |
crates/theme/src/registry.rs |
Loading, parsing, and storing theme metadata (ThemeRegistry) |
crates/theme/src/registry.rs |
crates/theme/src/styles/syntax.rs |
Definition and merging of SyntaxTheme and HighlightStyle |
crates/theme/src/styles/syntax.rs |
crates/theme/src/settings.rs |
Handling of theme_overrides from user settings |
crates/theme/src/settings.rs |
crates/theme_extension/src/theme_extension.rs |
Bridge for extensions to register bundled themes | crates/theme_extension/src/theme_extension.rs |
docs/src/extensions/themes.md |
Documentation of the JSON schema and extension layout | docs/src/extensions/themes.md |
docs/src/themes.md |
User-facing guide on installing and creating custom themes | docs/src/themes.md |
Summary
- Zed's theme system relies on a JSON-driven schema (
v0.2.0) that defines UI colors in thecolorsfield and syntax highlighting in thesyntaxfield. - The
ThemeRegistryincrates/theme/src/registry.rshandles automatic discovery and validation of theme files from~/.config/zed/themesor extension directories. - Syntax highlighting customization occurs through
SyntaxTheme::mergeincrates/theme/src/styles/syntax.rs, which combines base themes with usertheme_overridesor extension contributions. - Extensions register themes at runtime via
ThemeRegistryProxyincrates/theme_extension/src/theme_extension.rs, enabling hot-loading without restart.
Frequently Asked Questions
How do I create a custom theme for Zed?
Create a JSON file following the schema at https://zed.dev/schema/themes/v0.2.0.json, defining UI colors under "colors" and syntax styles under "syntax". Place this file in ~/.config/zed/themes on Linux/macOS or %APPDATA%\Zed\themes on Windows, and Zed will automatically load it on startup via the ThemeRegistry.
Can I override specific syntax highlighting colors without creating a full theme?
Yes, use the theme_overrides setting in your settings.json to specify individual token colors and styles. These overrides are merged with your active theme at runtime via SyntaxTheme::merge in crates/theme/src/styles/syntax.rs, allowing targeted customization without duplicating the entire theme definition.
How do extensions register custom themes in Zed?
Extensions bundle theme JSON files in an extensions/<name>/themes/ directory and use the ThemeRegistryProxy struct from crates/theme_extension/src/theme_extension.rs (line 21) to register them at runtime. This makes the themes immediately available in the Theme Selector without requiring a Zed restart.
What JSON schema version should I use for Zed themes?
Use version v0.2.0 of the Zed theme schema, accessible at https://zed.dev/schema/themes/v0.2.0.json. This schema defines the required structure for UI color definitions, syntax highlighting rules, and theme metadata including the theme name and type (light or dark).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →