Ghostty Fixes the Terminal Rendering Bugs That Break Claude Code on iTerm2
Ghostty Terminal Usage Experience
I've always used iTerm2 as my terminal for development, mainly because many people use it, its ecosystem is well-developed, and it's easy to find the theme configurations, plugins, etc., that I want. However, since I started using Coding Agents for programming, I've been spending more and more time in the terminal, so I needed a terminal more suitable for AI development. Ghostty is an AI development terminal recommended by Claude. I spent a little over half a day configuring it, and after using it for a while, the results have been quite satisfactory.
Why Choose Ghostty
| Feature | Description |
|---|---|
| GPU-Accelerated Rendering | Written in Zig, extremely low resource usage, silky-smooth rendering |
| Native Kitty Protocol | Supports image display, rich text, advanced shell integration |
| Quick Terminal | Global shortcut to summon a drop-down terminal without leaving the current window |
| Huge Scrollback | Supports tens of millions of lines, AI tool long outputs are no longer lost |
These are the highlighted features from Ghostty's official introduction. Simply put, it's lighter, free from memory leaks and rendering stutters, making it an ideal carrier for CLI-based AI tools (like Claude Code, Codex, OpenCode).
Ghostty vs iTerm2
If you've been using iTerm2, is it necessary to switch to Ghostty? Below is an analysis from three dimensions: architectural differences, regular development scenarios, and AI development scenarios.
1. Architectural Differences
| Dimension | iTerm2 | Ghostty |
|---|---|---|
| Language | Objective-C | Zig |
| Rendering Method | CPU Rendering (Core Graphics) | GPU Rendering (Metal on macOS) |
| UI Framework | Custom-drawn controls | Native system controls (AppKit) |
| Terminal Protocol | xterm compatible + private extensions | Kitty Protocol + xterm compatible |
The core difference lies in the rendering method: iTerm2 draws text frame-by-frame using the CPU, while Ghostty uses the Metal GPU for batch rendering. This means when terminal content refreshes rapidly—for example, when an AI tool streams a large amount of text output in the terminal—Ghostty's GPU can process the entire screen update at once, whereas iTerm2's CPU rendering produces intermediate frames, leading to flickering or stuttering.
2. Regular Program Development
For daily command running and coding (vim/nano/git): the improvement is not significant.
Honestly, if you're just using the terminal to run git, npm, ls, or write a few shell scripts, iTerm2 is completely sufficient. The perceived difference between CPU and GPU rendering is very small in low-frequency refresh scenarios. Only in the following scenarios might there be a noticeable improvement:
| Scenario | iTerm2 | Ghostty | Perceived Difference |
|---|---|---|---|
| Fast scrolling through large logs | Occasional tearing, dropped frames | Synchronized rendering, no tearing | Significant |
| Long-term use (>4h) | Memory slowly grows | Memory stable | Moderate |
| Neovim + LSP high-frequency refresh | Quick completion pop-ups occasionally leave artifacts | Smooth, no artifacts | Significant |
| Full-screen TUI tools (htop/lazygit) | Usable, occasional flicker | Silky smooth | Slight |
| Multiple tabs/split panes | Gets laggy with many tabs | Tab switching is smooth | Moderate |
3. AI Programming Development
On Claude Code's GitHub Issues page, you can find many rendering problems related to iTerm2.
| Issue | Problem |
|---|---|
| #72392 | Flicker-free rendering breaks Shift+Enter on iTerm2 |
| #56546 | Full-screen TUI mode scrolling is laggy/not smooth |
| #56275 | Chat history scrolling causes input box rendering errors, layout corruption |
| #52436 | Enabling NO_FLICKER causes Ctrl-G editor to output raw key codes |
| #51576 | Duplicate conversation output lines appear in the terminal UI |
| #44492 | CLAUDE_CODE_NO_FLICKER=1 causes /resume to fail to preview completely |
| #34765 | Scroll position resets to the top of the session during processing |
Regarding the rendering flicker issue, Claude Code once specifically provided the CLAUDE_CODE_NO_FLICKER=1 environment variable to mitigate flickering, but this switch itself introduces new problems in iTerm2, such as causing the Shift+Enter shortcut to fail and the editor to freeze. This indicates a compatibility issue between Claude Code's own TUI rendering engine and iTerm2's terminal protocol implementation.
Why doesn't Ghostty have these problems?
Synchronous Rendering:
Ghosttysupports the synchronous rendering feature of the Kitty protocol, ensuring that Claude Code's streaming output is composited on the GPU side before refreshing the screen, fundamentally eliminating flicker.iTerm2does not support this feature.Kitty Keyboard Protocol: Claude Code uses a rich set of shortcuts (Shift+Tab, Ctrl+O, Ctrl+B, etc.).
Ghosttynatively supports the Kitty keyboard protocol, making key recognition more accurate;iTerm2uses its own keyboard extensions, and some key combinations can conflict with AI tools.Alternate Screen Buffer: After enabling the alternate screen buffer with
CLAUDE_CODE_NO_FLICKER=1, it runs smoothly inGhostty, while iniTerm2it easily triggers the Issues mentioned above.
Actual Usage Feelings
In actual use, for ordinary streaming output, there isn't much difference between iTerm2 and Ghostty. This is mainly because modern computers are quite powerful, and even with CPU rendering, the intuitive feel isn't significantly different from GPU rendering. However, after a large amount of text output in the terminal, if you scroll the mouse to view history, iTerm2 has a noticeable stutter, while Ghostty remains very smooth.
Installing and Configuring Ghostty
Below are my personal favorite configurations, recommended for use as needed.
1. Install Ghostty
brew install --cask ghostty
2. Install Companion Tools
# Core tools
brew install zoxide # Smart directory jumping (replaces cd)
brew install yazi # Terminal file manager: file browsing, previewing, operations, etc.
3. Configuration
Configuration file path: ~/.config/ghostty/config
mkdir -p ~/.config/ghostty
# ============================================
# Ghostty Terminal Configuration
# Optimized for AI-assisted development (Claude Code / Codex)
# ============================================
# --- Font ---
font-family = "Maple Mono NF CN"
font-size = 14
font-thicken = true
adjust-cell-height = 4
# --- Theme ---
theme = Kanagawa Wave
# --- Window Appearance ---
background-opacity = 0.95
background-blur-radius = 20
macos-titlebar-style = transparent
macos-option-as-alt = true
window-padding-x = 14
window-padding-y = 10
window-save-state = always
window-inherit-working-directory = true
window-inherit-font-size = true
window-width = 120
window-height = 35
window-theme = auto
# --- Cursor ---
cursor-style = bar
cursor-style-blink = true
# --- Mouse ---
mouse-hide-while-typing = true
copy-on-select = clipboard
link-url = true
# --- Quick Terminal (Quake-style dropdown) ---
quick-terminal-position = top
quick-terminal-screen = mouse
quick-terminal-autohide = true
quick-terminal-animation-duration = 0.15
# --- Close Behavior ---
confirm-close-surface = false
# --- Security ---
clipboard-paste-protection = true
clipboard-paste-bracketed-safe = true
# --- Shell Integration ---
shell-integration = detect
shell-integration-features = cursor,sudo,no-title,ssh-env,ssh-terminfo,path
# --- Scrollback Buffer ---
scrollback-limit = 25000000
# ============================================
# Keybindings (SAND System)
# ============================================
# --- Global Quick Terminal ---
keybind = global:ctrl+grave_accent=toggle_quick_terminal
# --- Tab Management ---
keybind = super+t=new_tab
keybind = super+w=close_surface
keybind = super+shift+left=previous_tab
keybind = super+shift+right=next_tab
keybind = super+1=goto_tab:1
keybind = super+2=goto_tab:2
keybind = super+3=goto_tab:3
keybind = super+4=goto_tab:4
keybind = super+5=goto_tab:5
# --- Split Pane Management (SAND: Split/Across/Navigate/Destroy) ---
# S - Split
keybind = super+d=new_split:right
keybind = super+shift+d=new_split:down
# N - Navigate
keybind = super+alt+left=goto_split:left
keybind = super+alt+right=goto_split:right
keybind = super+alt+up=goto_split:top
keybind = super+alt+down=goto_split:bottom
# D - Destroy & Equalize
keybind = super+shift+e=equalize_splits
keybind = super+shift+enter=toggle_split_zoom
# --- Font Size ---
keybind = super+plus=increase_font_size:1
keybind = super+minus=decrease_font_size:1
keybind = super+zero=reset_font_size
# --- Scrollback Navigation ---
keybind = super+k=clear_screen
keybind = super+shift+arrow_up=jump_to_prompt:-1
keybind = super+shift+arrow_down=jump_to_prompt:1
# --- Reload Config ---
keybind = super+shift+comma=reload_config
4. Configure Yazi File Manager
Configuration directory: ~/.config/yazi/
mkdir -p ~/.config/yazi
Main Config ~/.config/yazi/yazi.toml
[mgr]
ratio = [1, 2, 5]
sort_by = "natural"
sort_sensitive = false
sort_reverse = false
sort_dir_first = true
linemode = "size"
show_hidden = false
show_symlink = true
scrolloff = 5
mouse_events = ["click", "scroll"]
title_format = "Yazi: {cwd}"
[preview]
max_width = 600
max_height = 900
image_filter = "lanczos3"
image_quality = 75
[opener]
edit = [
{ run = 'code %s', desc = "VSCode", for = "unix" },
]
open = [
{ run = 'open %s', desc = "Open", for = "macos" },
]
reveal = [
{ run = 'open -R %1', desc = "Reveal in Finder", for = "macos" },
]
[open]
prepend_rules = [
{ mime = "text/*", use = ["edit", "open", "reveal"] },
{ mime = "application/json", use = ["edit", "open", "reveal"] },
{ mime = "*/javascript", use = ["edit", "open", "reveal"] },
{ mime = "*/typescript", use = ["edit", "open", "reveal"] },
{ mime = "*/x-yaml", use = ["edit", "open", "reveal"] },
]
[tasks]
micro_workers = 10
macro_workers = 25
bizarre_retry = 5
[plugin]
prepend_fetchers = [
{ id = "git", url = "*", run = "git", prio = "normal", group = "git" },
]
Keymap ~/.config/yazi/keymap.toml
[[manager.prepend_keymap]]
on = ["g", "h"]
run = "cd ~"
desc = "Go to home directory"
[[manager.prepend_keymap]]
on = ["g", "c"]
run = "cd ~/.config"
desc = "Go to config directory"
[[manager.prepend_keymap]]
on = ["g", "d"]
run = "cd ~/Downloads"
desc = "Go to downloads"
[[manager.prepend_keymap]]
on = ["g", "w"]
run = "cd ~/work"
desc = "Go to work directory"
[[manager.prepend_keymap]]
on = ["g", "D"]
run = "cd ~/Desktop"
desc = "Go to desktop"
[[manager.prepend_keymap]]
on = ["g", "t"]
run = "cd /tmp"
desc = "Go to tmp"
Theme ~/.config/yazi/theme.toml
[mode]
normal_main = { fg = "black", bg = "blue", bold = true }
normal_alt = { fg = "blue", bg = "reset", bold = true }
select_main = { fg = "black", bg = "green", bold = true }
select_alt = { fg = "green", bg = "reset", bold = true }
unset_main = { fg = "black", bg = "red", bold = true }
unset_alt = { fg = "red", bg = "reset", bold = true }
[status]
sep_left = { open = "", close = "" }
sep_right = { open = "", close = "" }
overall = { fg = "reset", bg = "reset" }
[filetype]
rules = [
{ mime = "image/*", fg = "magenta" },
{ mime = "video/*", fg = "yellow" },
{ mime = "audio/*", fg = "yellow" },
{ mime = "application/zip", fg = "red" },
{ mime = "application/gzip", fg = "red" },
{ mime = "application/x-tar", fg = "red" },
{ mime = "application/pdf", fg = "cyan" },
{ mime = "application/*doc*", fg = "green" },
{ name = "*", fg = "reset" },
{ name = "*/", fg = "blue", bold = true },
]
5. Update Shell Configuration
Append the following to the end of ~/.zshrc:
# =================== AI Development Optimizations ===================
# Claude Code: enable alternate screen buffer for smoother rendering
export CLAUDE_CODE_NO_FLICKER=1
# Hide agnoster theme username@hostname (cleaner prompt)
DEFAULT_USER="$USER"
# =================== Ghostty Title ===================
if [[ -n "${GHOSTTY_RESOURCES_DIR:-}" ]]; then
ghostty_set_title() {
local dir="${PWD/#$HOME/~}"
printf '\033]2;%s\033\\' "$dir"
}
autoload -Uz add-zsh-hook
add-zsh-hook chpwd ghostty_set_title
add-zsh-hook precmd ghostty_set_title
add-zsh-hook preexec ghostty_set_title
ghostty_set_title
fi
# =================== Yazi File Manager ===================
function y() {
local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd
yazi "$@" --cwd-file="$tmp"
if cwd="$(command cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
builtin cd -- "$cwd"
fi
rm -f -- "$tmp"
}
# =================== Zoxide Smart Directory Jump ===================
eval "$(zoxide init zsh)"
Configuration Notes:
| Config | Purpose |
|---|---|
CLAUDE_CODE_NO_FLICKER=1 |
Enables alternate screen buffer, Claude Code renders without flicker, constant memory usage |
DEFAULT_USER |
Hides user@host in agnoster theme, making the prompt cleaner |
Ghostty Title |
Automatically sets the terminal title to the current directory, making it easy to distinguish multiple tabs |
y() function |
Automatically cds to the last browsed directory when exiting Yazi |
zoxide init |
Initializes smart directory jumping |
Appendix: Keybinding Summary
Ghostty Splits & Tabs
| Action | Shortcut |
|---|---|
| New Tab | Cmd+T |
| Close Pane/Tab | Cmd+W |
| Switch Tab | Cmd+Shift+←/→ |
| Jump to Tab N | Cmd+1~5 |
| Split Right | Cmd+D |
| Split Down | Cmd+Shift+D |
| Navigate Panes | Cmd+Alt+Arrow Keys |
| Equalize Panes | Cmd+Shift+E |
| Toggle Pane Zoom | Cmd+Shift+Enter |
| Global Quick Terminal | Ctrl+`` |
| Reload Config | Cmd+Shift+, |
Ghostty Scrolling & Navigation
| Action | Shortcut |
|---|---|
| Clear Screen | Cmd+K |
| Jump to Previous Command | Cmd+Shift+↑ |
| Jump to Next Command | Cmd+Shift+↓ |
| Scroll to Top | Cmd+Home |
| Scroll to Bottom | Cmd+End |
| Page Up/Down | Cmd+PageUp/PageDown |
Zoxide
| Command | Description |
|---|---|
z work |
Jump to the most frequently visited directory containing "work" |
z foo bar |
Jump to a directory matching both "foo" and "bar" |
zi work |
Interactively select a match |
z -l |
List all recorded directories |
Yazi
| Command/Key | Description |
|---|---|
y |
Launch Yazi, auto cd on exit |
gh / gc / gd / gw |
Quick jump (inside Yazi) |
Enter |
Enter directory / Open file |
Space |
Select file |
y / x / p |
Copy / Cut / Paste |
d / D |
Move to trash / Permanently delete |
/ |
Search |
q |
Quit |