Speeding Up My ZSH Shell ⚡
Super quick one I want to document here! I got myself on a side quest, again! No biggie, my ZSH shell was taking ages to load. When I say ages, more like 5+ seconds every time I opened a new terminal, that sort of thing can add up. This is just something I’ve lived with over the years, nothing has prompted this other than me wondering why it’s slow, then searching for how to profile it.
How to profile your ZSH
So, what’s actually slowing things down? Zsh comes with this super
handy profiling tool called zprof
. Here’s how to use it:
# Add this to the TOP of your .zshrc
zmodload zsh/zprof
# Add this to the BOTTOM of your .zshrc
zprof
This give a load of output, but it’s a good starting point.
I had no idea this existed so massive shout out to Jacek’s Blog for this tip!
Ok, so what was making my shell slow?
When I ran the profiler, here’s what I found:
1) _omz_source 55.73% # Oh-My-Zsh loading everything
2) compinit 30.76% # Completion system being slow
3) syntax-highlight 14.63% # Making things pretty, but slow
So, Oh-My-Zsh was taking up over half the startup time!
Oh-My-Zsh (55.73% → ~20%)
According to JonLuca’s research, this can cut the load time in half! The auto-updates are nice, but I’d rather do them manually when I want to.
# Top of .zshrc
DISABLE_AUTO_UPDATE="true"
DISABLE_MAGIC_FUNCTIONS="true"
DISABLE_COMPFIX="true"
Fixing the completion system (30.76% → ~10%)
The completion system (compinit
) is zsh’s built-in command
completion - it’s what shows possible completions when you hit tab.
This is a neat trick I found. Instead of rebuilding the completion
cache every time, we only do it once a day:
# Smarter completion initialization
autoload -Uz compinit
if [ "$(date +'%j')" != "$(stat -f '%Sm' -t '%j' ~/.zcompdump 2>/dev/null)" ]; then
compinit
else
compinit -C
fi
This comes from a popular GitHub gist - cheers for sharing this one!
Making Spaceship Prompt Faster (22.47% → ~5%)
I love Spaceship prompt (been using it for years!), but it was being a bit of a resource hog. Here’s how to speed it up:
SPACESHIP_PROMPT_ASYNC=true
SPACESHIP_PROMPT_ADD_NEWLINE=true
SPACESHIP_CHAR_SYMBOL="⚡"
# Only load what you actually use
SPACESHIP_PROMPT_ORDER=(
time
user
dir
git
line_sep
char
)
The Spaceship team actually recommended this approach - only load what you need!
Plugin management
I have very few plugins, here’s how I organize my plugins now:
plugins=(
git
zsh-autosuggestions
zsh-syntax-highlighting # Always last!
)
The order is super important here! According to the zsh-syntax-highlighting docs, it needs to be last to work properly.
For autosuggestions, I added these performance tweaks:
ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE="20"
ZSH_AUTOSUGGEST_USE_ASYNC=1
The results? FAST! ⚡
Check this out:
Component | Before | After |
---|---|---|
Oh-My-Zsh | 55.73% | ~20% |
Completions | 30.76% | ~10% |
Syntax Highlight | 14.63% | ~8% |
Total Time | ~5s | ~0.5s |
That’s like a 10x improvement! My terminal now opens in the blink of an eye!
Want to try this yourself?
Add the profiling code (I showed you earlier) to see what’s slow
Try the fixes one at a time - that way you know what’s actually helping
Keep what works for you - everyone’s setup is different!
Before and after
For comparison here’s a before and after configs:
# Path to your Oh My Zsh installation.
export ZSH="$HOME/.oh-my-zsh"
# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes
ZSH_THEME="spaceship"
# spaceship config
SPACESHIP_PROMPT_ASYNC=false
SPACESHIP_PROMPT_ADD_NEWLINE="true"
SPACESHIP_CHAR_SYMBOL="⚡"
# Which plugins would you like to load?
# Standard plugins can be found in $ZSH/plugins/
# Custom plugins may be added to $ZSH_CUSTOM/plugins/
# Example format: plugins=(rails git textmate ruby lighthouse)
# Add wisely, as too many plugins slow down shell startup.
plugins=(
git
zsh-syntax-highlighting
zsh-autosuggestions
)
source $ZSH/oh-my-zsh.sh
# User configuration
# auto suggest
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#663399,standout"
#-------- Global Alias {{{
globalias() {
if [[ $LBUFFER =~ '[a-zA-Z0-9]+$' ]]; then
zle _expand_alias
zle expand-word
fi
zle self-insert
}
zle -N globalias
bindkey " " globalias # space key to expand globalalias
# bindkey "^ " magic-space # control-space to bypass completion
bindkey "^[[Z" magic-space # shift-tab to bypass completion
bindkey -M isearch " " magic-space # normal space during searches
. ~/.zsh_aliases
#}}}
# Start SSH agent if not running
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)" > /dev/null
ssh-add ~/.ssh/id_github_sign_and_auth 2>/dev/null
fi
# volta
export VOLTA_HOME="$HOME/.volta"
export PATH="$VOLTA_HOME/bin:$PATH"
# volta end
# bun completions
#[ -s "/home/scott/.bun/_bun" ] && source "/home/scott/.bun/_bun"
# Turso
export PATH="$PATH:/home/scott/.turso"
With all the optimisations in place, here’s the after config:
# Performance optimizations
DISABLE_AUTO_UPDATE="true"
DISABLE_MAGIC_FUNCTIONS="true"
DISABLE_COMPFIX="true"
# Cache completions aggressively
autoload -Uz compinit
if [ "$(date +'%j')" != "$(stat -f '%Sm' -t '%j' ~/.zcompdump 2>/dev/null)" ]; then
compinit
else
compinit -C
fi
# Oh My Zsh path
export ZSH="$HOME/.oh-my-zsh"
# Theme config - fixed syntax
ZSH_THEME="spaceship"
# Spaceship settings (fixed syntax)
SPACESHIP_PROMPT_ASYNC=true
SPACESHIP_PROMPT_ADD_NEWLINE=true
SPACESHIP_CHAR_SYMBOL="⚡"
# Minimal spaceship sections for performance
SPACESHIP_PROMPT_ORDER=(
time
user
dir
git
line_sep
char
)
# Carefully ordered plugins (syntax highlighting must be last)
plugins=(
git
zsh-autosuggestions
zsh-syntax-highlighting
)
# Source Oh My Zsh
source $ZSH/oh-my-zsh.sh
# Autosuggest settings
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#663399,standout"
ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE="20"
ZSH_AUTOSUGGEST_USE_ASYNC=1
# Alias expansion function
globalias() {
if [[ $LBUFFER =~ '[a-zA-Z0-9]+$' ]]; then
zle _expand_alias
zle expand-word
fi
zle self-insert
}
zle -N globalias
bindkey " " globalias
bindkey "^[[Z" magic-space
bindkey -M isearch " " magic-space
# Lazy load SSH agent
function _load_ssh_agent() {
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)" > /dev/null
ssh-add ~/.ssh/id_github_sign_and_auth 2>/dev/null
fi
}
autoload -U add-zsh-hook
add-zsh-hook precmd _load_ssh_agent
# Path configurations
export VOLTA_HOME="$HOME/.volta"
PATH="$VOLTA_HOME/bin:$PATH:/home/scott/.turso"
export PATH
# Source aliases last
[ -f ~/.zsh_aliases ] && source ~/.zsh_aliases
Other things
As per usual when I do this sort of thing I alway search around for
alternitives, I tried Starship and to be
honest, I’m pretty happy with zsh, what it was for me (when trying it)
is I don’t use cd
I’m so used to just entering the directory name I
was pretty put off straight away. 😅
There were other things out there, like:
- Pure prompt (super fast alternative to Spaceship)
- fast-syntax-highlighting (potentially faster than regular syntax highlighting, though the default is pretty quick already!)
- Zinit (a faster alternative to Oh-My-Zsh if you want to try something different)
But, honestly? If you’re happy with your current setup (and I am) and it’s fast enough, stick with it! This was me going on a little side quest because I was curious. My curiosity was rewarded with a much faster shell!
That’s it! Hope this helps if you were having similar issues!
There's a reactions leaderboard you can check out too.