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.
First, measure
Section titled “First, measure”Before touching anything, get a baseline:
time zsh -i -c exitRun 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.
What Oh My Zsh actually does for you
Section titled “What Oh My Zsh actually does for you”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
sourcelines. - 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.
A .zshrc worth stealing
Section titled “A .zshrc worth stealing”This is the whole thing. Create or replace ~/.zshrc:
# ~/.zshrc — the whole thing
## HistoryHISTFILE=~/.zsh_historyHISTSIZE=50000SAVEHIST=50000setopt SHARE_HISTORY # one history shared across all sessionssetopt HIST_IGNORE_DUPS # no repeated entriessetopt HIST_IGNORE_SPACE # " secret-command" never touches historysetopt HIST_REDUCE_BLANKS
## Completion: rebuild the cache daily, not once per shellautoload -Uz compinitif [[ -n ~/.zcompdump(#qN.mh+24) ]]; then compinitelse compinit -Cfizstyle ':completion:*' menu select # arrow through completionszstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}' # case-insensitive
## Quality of lifesetopt AUTO_CD # type a directory name to cd to itsetopt INTERACTIVE_COMMENTS # allow # comments at the promptsetopt NO_BEEPbindkey -e # readline-style keys, whatever $EDITOR says
## Toolseval "$(starship init zsh)"eval "$(zoxide init zsh)"eval "$(fzf --zsh)"
## Plugins (install below; keep syntax-highlighting last)source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zshsource ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zshThe lines that earn their keep, in order of subtlety:
- The
compinitdance. 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 usescompinit -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>findsDocuments. 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 withCtrl+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.
Plugins without a plugin manager
Section titled “Plugins without a plugin manager”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:
brew install zsh-autosuggestions zsh-syntax-highlightingThen adjust the two source lines in your .zshrc to point at Homebrew’s copies:
source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zshsource $(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zshmkdir -p ~/.zshgit clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestionsgit clone https://github.com/zsh-users/zsh-syntax-highlighting ~/.zsh/zsh-syntax-highlightingThe source lines in the config above already point here. Updating is git -C ~/.zsh/zsh-autosuggestions pull whenever you feel like it — which is to say your plugins
never change beneath you, a feature or a liability depending on your relationship with supply
chains.
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.
Aliases worth stealing
Section titled “Aliases worth stealing”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:
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 pack — eza and bat are worth the upgrade.
When to keep the framework
Section titled “When to keep the framework”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.
Where next
Section titled “Where next”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.