Skip to content

Zsh Without the Bloat: A Fast, Minimal .zshrc

Somewhere between your first Mac and your second job, configuring Zsh stopped being something you do and became something you install. Oh My Zsh has been the default answer since 2009: three hundred plugins, a hundred and forty themes, and — if you’re not careful — a shell startup time you could measure with a sundial.

Here’s what the install script doesn’t mention: the useful core of all that is about forty lines of configuration. This guide builds a fast, minimal .zshrc that keeps the parts people actually love — history that works, completion that doesn’t need a manual, the grey ghost text — and drops the rest. No framework, no magic, and a prompt that appears before your finger has left the Enter key.

Before touching anything, get a baseline:

Terminal window
time zsh -i -c exit

Run it a few times and take the best. This starts an interactive zsh, exits immediately, and tells you what your current setup costs per shell. Note the number — you’ll want it for the smug comparison at the end.

Credit where due: Oh My Zsh isn’t a scam. It’s a bundle, and bundles are how you end up paying for channels you don’t watch. Unpack it and you get:

  • Sensible defaults for history and completion — which we’re about to set directly, in six lines.
  • A prompt theme engine — replaced, better and cross-shell, by Starship.
  • Plugin loading — which, for the two plugins that matter, is two source lines.
  • The git plugin’s aliases — genuinely beloved, and the full list is public. Steal your favourites; we’ll get to that.
  • Three hundred other plugins and 140 themes you will never enable.

The real cost isn’t even the startup time — it’s the opacity. Most framework users can’t say what their shell is actually doing. The config below is one file you understand completely.

This is the whole thing. Create or replace ~/.zshrc:

Terminal window
# ~/.zshrc — the whole thing
## History
HISTFILE=~/.zsh_history
HISTSIZE=50000
SAVEHIST=50000
setopt SHARE_HISTORY # one history shared across all sessions
setopt HIST_IGNORE_DUPS # no repeated entries
setopt HIST_IGNORE_SPACE # " secret-command" never touches history
setopt HIST_REDUCE_BLANKS
## Completion: rebuild the cache daily, not once per shell
autoload -Uz compinit
if [[ -n ~/.zcompdump(#qN.mh+24) ]]; then
compinit
else
compinit -C
fi
zstyle ':completion:*' menu select # arrow through completions
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}' # case-insensitive
## Quality of life
setopt AUTO_CD # type a directory name to cd to it
setopt INTERACTIVE_COMMENTS # allow # comments at the prompt
setopt NO_BEEP
bindkey -e # readline-style keys, whatever $EDITOR says
## Tools
eval "$(starship init zsh)"
eval "$(zoxide init zsh)"
eval "$(fzf --zsh)"
## Plugins (install below; keep syntax-highlighting last)
source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
source ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

The lines that earn their keep, in order of subtlety:

  • The compinit dance. By default zsh audits your completion directories on every shell start. The glob qualifier here runs the full rebuild only when the cache is more than a day old, and uses compinit -C — audit skipped — the other 99% of the time. This one block is usually worth a few hundred milliseconds on its own.
  • matcher-list. Case-insensitive completion: cd Doc<TAB> finds Documents. The way it should have worked all along.
  • SHARE_HISTORY. One history across every window, tab and tmux pane, appended as you go. Pairs beautifully with Ctrl+R — and even better with fzf’s fuzzy version.
  • HIST_IGNORE_SPACE. Prefix a command with a space and it never enters history. For commands containing things they shouldn’t.
  • bindkey -e. Zsh takes a worrying number of cues from $EDITOR; if that’s vim, you get vi key bindings at the prompt and a confusing afternoon. This pins the readline-style bindings from our shortcuts cheat sheet.

The tool init lines assume you’ve installed Starship, zoxide and fzf — delete any you’re not using, though you’d be missing out.

Two plugins earn their place in a minimal setup:

  • zsh-autosuggestions — the grey ghost text that completes commands from your history. The single plugin people actually miss.
  • zsh-syntax-highlighting — commands turn green when valid and red when you’ve typed rubbish. A linter for your prompt.

Install them directly:

Terminal window
brew install zsh-autosuggestions zsh-syntax-highlighting

Then adjust the two source lines in your .zshrc to point at Homebrew’s copies:

Terminal window
source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh
source $(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

One rule: syntax-highlighting must be sourced last — it hooks the line editor and expects to be the final word. The config above already has them in the right order.

If you later outgrow manual sourcing and want pinning and lazy loading, antidote and sheldon are the grown-up plugin managers. But two plugins don’t need a manager any more than two books need a librarian.

The Oh My Zsh git plugin’s alias list is its most-loved feature, and it’s just a text file. Raid it for your favourites:

Terminal window
alias gst='git status'
alias gco='git checkout'
alias gd='git diff'
alias gp='git push'
alias gl='git pull'

For ls and cat, skip the aliases entirely and install the modern replacements from our CLI starter packeza and bat are worth the upgrade.

If your Oh My Zsh setup starts in 100ms and sparks joy, keep it. The point of all this isn’t that frameworks are wicked — it’s that they’re optional, and that forty understood lines beat four hundred inherited ones. Run the time command again now, and let the number decide.

That’s the shell itself sorted. Complete the stack: a prompt that earns its keep, the modern CLI starter pack for the tools you’ll be typing at it, the keyboard shortcuts worth memorising now that bindkey -e has made them all work, and tmux to keep your sessions alive when the Wi-Fi won’t. And once it’s all in place, put it in git before the disk dies.