跪拜 Guibai
← Back to the summary

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?

  1. Synchronous Rendering: Ghostty supports 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. iTerm2 does not support this feature.

  2. Kitty Keyboard Protocol: Claude Code uses a rich set of shortcuts (Shift+Tab, Ctrl+O, Ctrl+B, etc.). Ghostty natively supports the Kitty keyboard protocol, making key recognition more accurate; iTerm2 uses its own keyboard extensions, and some key combinations can conflict with AI tools.

  3. Alternate Screen Buffer: After enabling the alternate screen buffer with CLAUDE_CODE_NO_FLICKER=1, it runs smoothly in Ghostty, while in iTerm2 it 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

Rendering effect reference

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