A Practical Ghostty-Based Terminal Stack
A practical macOS terminal stack built around Ghostty, JetBrainsMono Nerd Font, zinit, Starship, zoxide, fzf, Yazi, and modern CLI tools, focused on startup performance, readability, history preservation, and reproducible setup.
A Practical Ghostty-Based Terminal Stack
A good terminal setup should not feel heavy. It should start quickly, render text and icons reliably, preserve command history, make navigation fast, and improve the readability of everyday commands.
The stack I settled on is:
Ghostty + JetBrainsMono Nerd Font + zinit + Starship + zoxide + fast-syntax-highlighting + zsh-autosuggestions + fzf + Yazi + eza/ripgrep/fd/bat/btop/duf/httpie.
The goal is not to build a large shell framework. The goal is to replace a traditional Terminal.app + Oh My Zsh + directly sourced plugins setup with a clearer, faster, reproducible terminal environment.
The Stack At A Glance
flowchart LR
A[Ghostty<br/>GPU accelerated terminal] --> B[JetBrainsMono Nerd Font<br/>icons and monospace rendering]
B --> C[zsh startup]
C --> D[zinit<br/>lazy plugin loading]
D --> E[zsh-autosuggestions<br/>history-based suggestions]
D --> F[fast-syntax-highlighting<br/>command highlighting]
C --> G[zoxide<br/>smart cd]
C --> H[Starship<br/>cross-shell prompt]
C --> I[modern CLI aliases<br/>eza rg fd bat btop duf]
C --> J[fzf<br/>key bindings and completion]
C --> K[Yazi<br/>terminal file manager]
The key idea is separation of responsibility. The terminal emulator handles rendering. The font handles icons. zsh stays small. zinit loads interactive plugins lazily. Starship owns the prompt. zoxide owns navigation. Modern CLI tools improve the commands used every day.
From The Old Setup To The New Setup
A common old setup looks like this:
flowchart TB
subgraph Old[Old setup]
O1[Terminal.app] --> O2[Oh My Zsh]
O2 --> O3[theme]
O2 --> O4[directly sourced autosuggestions]
O2 --> O5[directly sourced syntax highlighting]
end
subgraph New[New setup]
N1[Ghostty] --> N2[lean zsh]
N2 --> N3[zinit lazy plugins]
N2 --> N4[Starship prompt]
N2 --> N5[zoxide + fzf]
N2 --> N6[modern CLI tools]
end
The migration does not need to delete Oh My Zsh or old plugins. A safer approach is to comment out the old loading lines so they no longer affect shell startup. The files remain available for rollback.
Tools And Plugins
| Component | Role | Core capability | Why it is in the stack |
|---|---|---|---|
| Ghostty | Terminal emulator | GPU/Metal rendering, native macOS behavior, file-based config | More modern and reproducible than the stock Terminal setup |
| JetBrainsMono Nerd Font | Font | Monospace text, Nerd Font icons, Powerline symbols | Keeps Starship and eza icons rendering correctly |
| zinit | zsh plugin manager | Lazy loading and plugin management | Replaces heavier framework-style loading |
| zsh-autosuggestions | Input suggestions | Grey suggestions from shell history | Keeps the familiar history-based workflow |
| fast-syntax-highlighting | Syntax highlighting | Highlights commands, arguments, and paths | Fast enough for interactive use |
| Starship | Prompt | Git status, directory, language versions, command duration | Cross-shell, compact, and easy to configure |
| zoxide | Navigation | Frequency-aware directory jumping | Preserves the cd habit while adding smart jumps |
| fzf | Fuzzy finder | Ctrl-R history search, fuzzy completion, file selection | Speeds up high-frequency interactive tasks |
| Yazi | Terminal file manager | Three-pane browsing, previews, file operations, return-to-directory workflow | Bridges Finder-like browsing and shell navigation inside the terminal |
| eza | ls replacement | Icons, Git status, tree view | Restores and improves ll/la/l/lt shortcuts |
| ripgrep | grep replacement | Fast full-text search | Better default for code and text search |
| fd | find replacement | Simple syntax and sensible ignores | Easier day-to-day file discovery |
| bat | cat replacement | Syntax highlighting and paging | More readable file inspection |
| btop | top replacement | Interactive CPU, memory, and process view | Better system monitoring |
| duf | df replacement | Human-friendly disk usage tables | Easier disk inspection |
| httpie | HTTP client | Readable HTTP requests and responses | Friendly API debugging |
Alias Design
| Traditional command | Replacement | Intent |
|---|---|---|
ls | eza --icons=auto --group-directories-first | Keep the habit, improve output |
ll | eza -lh --icons=auto --group-directories-first --git | Long listing with Git status |
la | eza -lah --icons=auto --group-directories-first --git | Include hidden files |
l | eza -1 --icons=auto --group-directories-first | Fast one-column listing |
lt | eza --tree --level=2 --icons=auto --group-directories-first | Shallow tree view |
cat | bat | Syntax-highlighted reading by default |
grep | rg | Faster text search |
find | fd | Simpler file search |
top | btop | Modern resource monitor |
df | duf | Readable disk usage |
y | yazi | Open the terminal file manager quickly |
yy | wrapper around yazi --cwd-file | Change the current shell to Yazi’s final directory after exit |
These aliases should be conditional. If a tool is missing on a new machine, the shell should still start.
Yazi previews work best with a few supporting tools: ffmpegthumbnailer for video thumbnails, sevenzip for archives, poppler for PDFs, imagemagick for image processing, and jq for JSON. Installing them with the terminal stack keeps Yazi useful immediately on a fresh machine.
Preserve History
Command history is part of the working environment. A terminal migration should not lose it. The setup keeps using the existing ~/.zsh_history:
export HISTFILE="${HISTFILE:-$HOME/.zsh_history}"
export HISTSIZE=100000
export SAVEHIST=100000
setopt APPEND_HISTORY
setopt EXTENDED_HISTORY
setopt INC_APPEND_HISTORY
setopt SHARE_HISTORY
setopt HIST_IGNORE_DUPS
setopt HIST_REDUCE_BLANKS
setopt HIST_VERIFY
After opening a new Ghostty window, the old history remains available to zsh, fzf, and zsh-autosuggestions.
Why Not Keep Adding To Oh My Zsh
Oh My Zsh is excellent for getting started. Over time, however, it can make the startup path harder to reason about. Themes, plugins, completion behavior, paste behavior, and framework defaults can become entangled.
This stack takes a narrower approach:
| Goal | Approach |
|---|---|
| Faster startup | Lazy-load interactive plugins with zinit |
| Maintainable prompt | Use Starship for prompt rendering |
| History preservation | Keep ~/.zsh_history as the history file |
| Rollback safety | Comment old config instead of deleting it |
| Cleaner automation | Load TTY-only integrations only in real terminals |
| Better daily commands | Replace legacy commands with modern CLI tools |
Theme Preferences
Ghostty theme names must come from ghostty +list-themes. Invalid names fail config loading. My preferred dark themes are:
| Priority | Theme | Style |
|---|---|---|
| 1 | TokyoNight Night | Deep Tokyo Night style, calm and suitable for long coding sessions |
| 2 | TokyoNight Storm | Similar family with slightly different contrast |
| 3 | Terminal Basic Dark | Closer to the classic macOS Terminal dark look |
The current default is:
theme = "TokyoNight Night"
Always validate after changing it:
ghostty +validate-config --config-file="$HOME/.config/ghostty/config"
Idempotent Installation
This setup works best as a repeatable installer. The installer should:
- Check macOS and Homebrew.
- Install Ghostty, Nerd Font, zinit, Starship, zoxide, fzf, Yazi, and modern CLI tools.
- Comment legacy Oh My Zsh and direct plugin loading lines instead of deleting them.
- Manage
~/.zshrcwith marker blocks so reruns replace the block instead of appending duplicates. - Write Ghostty, Starship, and Yazi configs.
- Prime zinit plugins once so the first real terminal does not wait on cloning.
- Validate
~/.zshrcand Ghostty config.
flowchart TD
A[Run installer] --> B{Homebrew available?}
B -- No --> C[Ask user to install Homebrew]
B -- Yes --> D[Install formulae and casks]
D --> E[Backup and update zshrc]
E --> F[Write Ghostty config]
F --> G[Write Starship and Yazi configs]
G --> H[Prime zinit plugins]
H --> I[Validate zsh, Yazi, and Ghostty config]
I --> J[Open a new Ghostty window]
Why This Setup Works
The result is a clean terminal stack:
- Ghostty and Nerd Font handle rendering.
- zsh keeps only essential shell behavior.
- zinit handles lazy plugin loading.
- Starship handles the prompt.
- zoxide handles navigation.
- fzf handles fuzzy interaction.
- Yazi handles terminal-native file browsing and previews.
- Modern CLI tools handle readable listings, search, file inspection, resource monitoring, disk usage, and HTTP debugging.
- Old config remains available but no longer participates in startup.
This is not about making the terminal flashy. It is about making the interaction path explicit: rendering, completion, search, prompt, navigation, and command readability each have a clear owner. That makes the environment easier to migrate, debug, and maintain.