From VS Code (and friends) to Zed
But why?
Have you seen the news lately? VSCode is becoming a bit of a liability.
- GitHub breached via poisoned Nx Console extension (May 2026)
- MaliciousCorgi — fake AI assistants stealing source code (January 2026)
- Shai-Hulud 2.0 worm — AsyncAPI compromise (November 2025)
- Prettier impersonation → Anivia loader → OctoRAT (November 2025)
- GlassWorm self-propagating campaign (October 2025 – April 2026)
- 550+ secrets leaked across 500+ extensions (October 2025)
... and on and on.
One of the best things about VScode, the power, flexibility, breadth and depth of its plugins, is now one of its biggest weaknesses.
Similarly, the electron base that made it cross platform and easy to run, is kinda... big and slow.

I've tried to move to Zed a few times, but the final nail was my VSCode flavor of choice (Windsurf) finally going "full AI", and becoming Devin Desktop.

That was the final push I needed.
Shut up and setup my Zed
Okay, okay. Fork this and run install.sh:
It symlinks the config files into ~/.config/zed/ and installs Zed and JetBrains Mono Font via Homebrew.
Because it symlinks (not copies), any changes you make in Zed show up as git diffs in the repo for you to keep, discard the rest as needed. Your config is version-controlled as you go.
Otherwise, here it is in a bit more detail.
1. Install
Direct download (recommended) → zed.dev/download
Homebrew (Mac)
brew install --cask zedCLI tool (optional, but recomended) open projects from terminal:
# Command Palette → "zed: install CLI"
zed . # open current folder
zed file.ts # open a fileStable vs Preview: Stable is fine for daily use. Preview gets features ~1 week earlier but ships more bugs. You can install both and switch.
2. First-run: set your keymap
The single most important setting. Add to ~/.config/zed/settings.json:
{
"base_keymap": "VSCode"
}This gets you ~80% of muscle memory working immediately. E.g.⌘P (file picker), ⌘⇧P (command palette), ⌘D (multi-select), ⌘` (terminal).
The remaining gaps are covered in the shortcuts section below.
Other options: "JetBrains", "Atom", "Emacs", "SublimeText", "TextMate".
3. Extensions. Install these first
Open extensions: ⌘⇧X or Command Palette → zed: extensions
| Extension | What it does | Replaces |
|---|---|---|
| Codebook | Spell checker that understands camelCase and code identifiers | Code Spell Checker |
| Typos | Low-noise typo detection in identifiers, very few false positives | (no VS Code equivalent) |
| Colored Zed Icons Theme | The default Zed file icons, but coloured, makes the file tree much easier to scan | File icon themes in VS Code |
| Biome | Fast linter + formatter for JS/TS/JSON (replaces ESLint + Prettier) | ESLint, Prettier |
Skip the manual step: If you fork my-zed-config, Codebook, Biome, and Colored Zed Icons Theme are listed under `auto_install_extensions`. Makes Zed install them automatically on first launch.
On extension security: Zed extensions are compiled to WebAssembly with a restricted API surface. They cannot run arbitrary host processes the way VS Code extensions can. A meaningful security improvement over the VS Code extension model, which has seen a wave of supply-chain attacks in 2025–2026.
4. Themes
Switch themes live with ⌘K ⌘T, arrow keys preview in real time, Enter saves.
Built-in themes are decent (no install needed):
One Dark/One Lightare good defaults, most people stick with theseGruvbox Dark/Gruvbox Light/Gruvbox Dark HardAyu Dark/Ayu Light/Ayu Mirage
More themes available via ⌘⇧X and at zed-themes.com.
Icon themes: Zed supports icon themes separately from colour themes. Colored Zed Icons Theme is the most popular. File icons are nice, colours are better.
Install from ⌘⇧X and activate via Command Palette → theme selector: toggle icons.
Per-project theming: No Peacock (sadly). But you can set "theme" in .zed/settings.json at the repo root.
Markdown tip: theme choice significantly affects Markdown legibility. `Gruvbox Dark Hard` and `Ayu Dark` give the most visual distinction between headings, body text, and inline code. If Markdown looks flat, try switching theme before reaching for settings overrides.
5. Font
A good coding font makes a real difference. Some options:
| Font | Price | Character |
|---|---|---|
| JetBrains Mono | Free | Best all-rounder, 143 ligatures, excellent on Retina |
| Geist Mono | Free | Minimal, clean, no ligatures, "invisible" font |
| Berkeley Mono | $75 one-time | Premium craftsmanship, worth it if you live in an editor |
After installing, add to settings:
{
"buffer_font_family": "JetBrains Mono",
"buffer_font_size": 14,
"buffer_font_features": { "calt": true }
}6. Settings. My recommended base config
User settings: ~/.config/zed/settings.json, open with ⌘, Project settings: .zed/settings.json at repo root. Auto-loaded per project, merged with user settings. Great for per-repo formatter config, language server overrides, or exclusions.
{
"base_keymap": "VSCode",
"diff_view_style": "unified",
"theme": {
"mode": "system",
"light": "One Light",
"dark": "One Dark"
},
"buffer_font_family": "JetBrains Mono",
"buffer_font_size": 14,
"buffer_font_features": { "calt": true },
"ui_font_size": 14,
"soft_wrap": "editor_width",
"git": {
"inline_blame": { "enabled": true, "delay_ms": 800 }
},
"format_on_save": "on",
"ensure_final_newline_on_save": true,
"terminal": {
"font_family": "JetBrains Mono",
"font_size": 13,
"working_directory": "current_project_directory"
},
"file_scan_exclusions": [
"**/.git",
"**/node_modules",
"**/.next",
"**/dist",
"**/.turbo"
],
"show_whitespaces": "boundary",
"scrollbar": { "show": "auto" },
"indent_guides": { "enabled": true },
"auto_install_extensions": {
"colored-zed-icons-theme": true,
"biome": true,
"codebook": true
},
"languages": {
"Markdown": {
"language_servers": ["codebook"],
"soft_wrap": "editor_width"
}
}
}A few things worth noting in that block:
"theme"with"mode": "system". Follows your OS dark/light setting automatically. Much better than hardcoding"One Dark"."git": { "inline_blame": ... }.inline_blamemust live under thegitkey, not at the top level. Easy mistake, silent failure if you get it wrong.auto_install_extensions. Zed installs these on first launch. No marketplace hunting needed.
Example: Biome (linting + formatting)
If you use Biome for linting and formatting, configure it per-language rather than globally (to avoid it interfering with languages it doesn't support):
{
"languages": {
"TypeScript": {
"language_servers": ["biome", "!vtsls", "!typescript-language-server"],
"formatter": { "language_server": { "name": "biome" } },
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
}
},
"TSX": {
"language_servers": ["biome", "!vtsls"],
"formatter": { "language_server": { "name": "biome" } },
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
}
},
"JavaScript": {
"language_servers": ["biome", "!vtsls"],
"formatter": { "language_server": { "name": "biome" } },
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
}
},
"JSON": {
"language_servers": ["biome"],
"formatter": { "language_server": { "name": "biome" } }
}
}
}Don't forget TSX. Zed treats `TypeScript` and `TSX` as separate languages. If you only configure `TypeScript`, your `.tsx` files still get formatted by vtsls. Both need the Biome override.
Requires Biome v2+. Full setup guide: biomejs.dev/reference/zed
TypeScript inlay hints (vtsls)
If you're using vtsls (the default TypeScript server), it's worth enabling inlay hints. They show parameter names and return types inline, without hovering:
{
"lsp": {
"vtsls": {
"settings": {
"typescript": {
"inlayHints": {
"parameterNames": { "enabled": "literals" },
"returnTypes": { "enabled": true }
},
"tsserver": {
"maxTsServerMemory": 8192
}
}
}
}
}
}"literals" shows parameter names only when the argument is a literal value (string, number, boolean). Low noise, high signal. maxTsServerMemory is worth bumping on large monorepos; the default is 3072MB.
7. Git integration
This is where Zed stands out from most editors.
Git panel: ⌃G shows all changed files with status indicators, staged/unstaged sections, and a commit message box.
Project diff: ⌘⇧D opens every changed file in a single split-diff multibuffer (like a github PR). You can edit directly in the diff, stage individual hunks with a click, and navigate across file boundaries without switching tabs. This is the closest equivalent to Windsurf/Cursor's diff review flow.
Split vs unified diff: Zed ships with split diff as default; I prefer unified, easy to set "diff_view_style": "unified" in settings, or toggle on the fly with Command Palette → diff: toggle split diff.
Git graph: Command Palette → git: graph for a lazy-loading, searchable commit history with a commit info panel.
Inline blame: Shows on the current line after a short delay. Toggle with ⌘K ⌘I.
Branch switching: ⌘K ⌘B or Command Palette → git: switch
Worktrees: Supported natively. Command Palette → git: create worktree. Each worktree gets its own project panel context.
Multiple repos in one window: Zed's git panel doesn't fully support workspaces with multiple git repositories yet (#52191). The workaround: use Command Palette → project: add folder to project for each sub-repo. They appear in the file tree, and you can select between them in the git panel tab, not perfect, but functional.
Docs: zed.dev/docs/git
8. AI agents. Claude Code, OpenCode, and Zed's built-in agent
Zed runs external agents via ACP (Agent Client Protocol). It works well, where agent runs as an independent process with its own thread. You can run multiple agents simultaneously on different tasks.
Open the Agent Panel: ⌘?
Panel layout: Zed's default puts the agent panel and threads on the left, project/git panel on the right. Right-click any panel icon in the status bar to move it. Or set in settings:
{
"agent": {
"dock": "right",
"play_sound_when_agent_done": "always"
}
}"play_sound_when_agent_done": "always" is surprisingly handy. Pings you to come back when it's done.
You can also wire up both agents via settings, so they're available immediately after forking and installing:
{
"agent_servers": {
"opencode": { "type": "registry" },
"claude-acp": {
"type": "registry",
"default_config_options": {
"mode": "acceptEdits",
"model": "sonnet"
}
}
}
}acceptEdits means Claude Code auto-accepts its own edits without a confirmation step on every file, swap to plan mode if you want no edits. "model": "sonnet" keeps costs reasonable; switch to "opus" with /model as needed, I find sonnet to be enough most of the time.
Claude Code (via ACP)
Command Palette → "acp: open registry" → claude-acp → Install
Agent Panel → + → claude-acp → /login (first time)CLAUDE.md files in your repo are picked up automatically. Billing goes through your Anthropic account.
→ zed.dev/acp/agent/claude-agent
OpenCode (via ACP)
Command Palette → "acp: open registry" → OpenCode → Install
Agent Panel → + → OpenCodeOpenCode manages its own auth and model selection and supports 75+ LLM providers. Can also run as a raw TUI in the integrated terminal.
Reviewing agent diffs
When an agent makes changes, Zed shows them in the same editable multibuffer diff view as git. You can accept/reject individual hunks inline, or edit the diff directly before accepting.
Zed's built-in agent
Zed has a first-party agent (your choice of model: Claude, GPT, Gemini, local). Good for inline assist and quick questions.
- Inline assist:
⌘I→ applies changes inline at the cursor - Agent thread:
⌘?→ new thread. Full conversation with file context
For autonomous multi-file work, Claude Code or OpenCode via ACP are the better choice.
Docs: zed.dev/docs/ai/external-agents
9. Keyboard shortcuts
VS Code shortcuts that carry over (with "base_keymap": "VSCode")
| Action | Shortcut |
|---|---|
| Command Palette | ⌘⇧P |
| Quick open file | ⌘P |
| Open settings | ⌘, |
| Toggle terminal | ⌘` |
| Go to definition | F12 |
| Peek definition | ⌥F12 |
| Find in files | ⌘⇧F |
| Multi-cursor select next | ⌘D |
| Move line up/down | ⌥↑ / ⌥↓ |
| Duplicate line | ⌥⇧↓ |
| Delete line | ⌘⇧K |
| Toggle comment | ⌘/ |
| Format document | ⌥⇧F |
| Rename symbol | F2 |
| Close tab | ⌘W |
Zed-specific shortcuts to learn
| Action | Shortcut |
|---|---|
| Open git panel | ⌃G |
| Project diff (all changes) | ⌘⇧D |
| Toggle inline blame | ⌘K ⌘I |
| Switch theme | ⌘K ⌘T |
| Open agent panel | ⌘? |
| Inline AI assist | ⌘I |
| Open extensions | ⌘⇧X |
| Open keymap editor | ⌘K ⌘S |
| Toggle project panel | ⌘B |
| Split editor right | ⌘K ⌘→ |
| Open recent projects | ⌘⌥O |
| Toggle word wrap | ⌥Z |
| Open Markdown preview | ⌘⇧P (in a .md file) |
Shortcuts that changed
| Action | VS Code | Zed |
|---|---|---|
| Open recent | ⌃R |
⌘⌥O |
| Go to symbol | ⌘⇧O |
⌘⇧O |
| Outline panel | Explorer sidebar | Command Palette → outline panel |
Full cheat sheet: cheatsheets.zip/zed
Keybindings docs: zed.dev/docs/key-bindings
Official VS Code migration guide: zed.dev/docs/migrate/vs-code
10. Tips & tricks
Moving panels
Easy to miss, if you don't look for it. You can right-click any panel icon in the bottom status bar and tell it where to go. Zed writes the setting to your settings.json automatically. Alternatively set it manually:
{
"project_panel": { "dock": "left" },
"git_panel": { "dock": "left" },
"agent": { "dock": "right" },
"terminal": { "dock": "bottom" }
}Opening files in an external app with a single keypress
Useful when you want to open a specific file type in a dedicated app.
Yes you can use markdown preview with ⌘P, but I like WYSIWYG markdown, so have setup Typora. Same idea works for opening a CSV in TablePlus, or an image in Preview etc
Step 1. Define the task in `~/.config/zed/tasks.json`
[
{
"label": "Open in Typora",
"command": "open -a Typora \"$ZED_FILE\"",
"reveal": "never",
"allow_concurrent_runs": true
}
]$ZED_FILE is the absolute path to the currently focused file.
Gotcha: Put the full command in `command`, don't wrap it in `sh -c` with an `args` array. Zed already runs the command inside `zsh -i -c '...'`; the `args` field doesn't pass through as expected and silently does nothing.
Step 2. Bind a key in `~/.config/zed/keymap.json`:
[
{
"context": "Editor",
"bindings": {
"cmd-shift-t": ["task::Spawn", { "task_name": "Open in Typora" }]
}
}
]Gotcha 1: Use `task_name`, not `task_label`. With `task_label` the task picker opens (pre-filtered but still requiring Enter). With `task_name` it runs immediately.
Gotcha 2: Use `"context": "Editor"`, not `"Workspace"`. This ensures a file is focused (so `$ZED_FILE` is populated) and avoids conflicting with Zed's default `cmd-shift-t` binding.
Same pattern works for any app just change the label, app name, and key binding.
Markdown heading contrast booster - theme override
If headings look too flat in your theme, you can boost specific Markdown tokens without switching theme globally:
{
"languages": {
"Markdown": {
"theme_overrides": {
"syntax": {
"title": {
"color": "#e5c07b",
"font_weight": 700
},
"emphasis.strong": {
"font_weight": 800
},
"emphasis": {
"font_style": "italic"
}
}
}
}
}
}Adjust `#e5c07b` to taste. `title` is the Tree-sitter token for headings.
Autocomplete noise in Markdown files
A common frustration: Zed's autocomplete fires constantly while writing prose. Disable it for Markdown:
{
"languages": {
"Markdown": {
"show_completions_on_input": false
}
}
}11. Things you'll miss / workarounds
| What you had | Zed situation | Workaround |
|---|---|---|
| Peacock window tinting | Not built in | Set "theme" per repo in .zed/settings.json |
| Settings Sync | Not in Zed yet | Fork my-zed-config and run install.sh, it symlinks + git gives you version-controlled config (see section 12) |
| WYSIWYG Markdown (vscode-office / Obsidian-style) | Not supported in editor | Typora alongside Zed; or use the preview pane |
| Markdown PDF export | No built-in export | Typora; or pandoc CLI |
Markdown ⌘B bold shortcut |
Not implemented | Open feature request; use Typora for heavy doc editing |
| Extension marketplace breadth | Smaller ecosystem | Most critical extensions exist; gaps closing fast |
| Multi-root git panel | Partial support only (#52191) | project: add folder to project for each sub-repo, selectable in the git panel tab |
12. Useful links
| Resource | URL |
|---|---|
| Starter config (fork this) | github.com/a-c-m/my-zed-config |
| Download | zed.dev/download |
| Official docs | zed.dev/docs |
| VS Code migration guide | zed.dev/docs/migrate/vs-code |
| Extensions browser | zed.dev/extensions |
| Theme previews | zed-themes.com |
| Theme builder | zed.dev/theme-builder |
| Git docs | zed.dev/docs/git |
| External agents (ACP) | zed.dev/docs/ai/external-agents |
| ACP registry | zed.dev/acp |
| Claude Agent | zed.dev/acp/agent/claude-agent |
| OpenCode Agent | zed.dev/acp/agent/opencode |
| Keybindings docs | zed.dev/docs/key-bindings |
| Keyboard cheat sheet | cheatsheets.zip/zed |
| Biome extension | zed.dev/extensions/biome |
| Biome setup guide | biomejs.dev/reference/zed |
| GitHub repo | github.com/zed-industries/zed |
| Multi-root git tracking issue | github.com/zed-industries/zed/discussions/52191 |


