Zsh Configuration Tutorial: Customize Your Terminal with .zshrc, Aliases & Prompts
Video: Zsh Configuration Tutorial: Customize Your Terminal with .zshrc, Aliases & Prompts by Taught by Celeste AI - AI Coding Coach
Zsh Lesson 5: Customizing Zsh — Aliases, Env Vars, PROMPT, .zshrc
alias ll='ls -la'for shortcuts.export FOO=barfor child processes.PROMPT='%n@%m %~ %# 'customizes the prompt. Put everything in~/.zshrcto persist.source ~/.zshrcreloads.
Out of the box, Zsh works. With 20 minutes of customization, it feels yours.
Aliases
alias ll='ls -la'
ll
# (runs ls -la)
alias c='clear'
c
alias ..='cd ..'
..
alias name='command' creates a shortcut. Type name, the shell substitutes command.
Inspect:
alias ll
# ll='ls -la'
alias # all aliases
Aliases are per-session — only exist in the shell where you defined them. To persist, add them to ~/.zshrc.
Common useful aliases:
alias ll='ls -la'
alias la='ls -lah'
alias ..='cd ..'
alias ...='cd ../..'
alias gs='git status'
alias gd='git diff'
alias gl='git log --oneline -20'
alias k='kubectl'
alias dc='docker compose'
alias rm='rm -i' # always confirm
alias mv='mv -i'
alias cp='cp -i'
alias grep='grep --color=auto'
Aliases save keystrokes; they don't make you a wizard. Don't overdo it — too many cryptic aliases makes shells unfamiliar to anyone else (or future you).
Environment variables
echo $HOME
# /Users/alice
echo $USER
# alice
echo $SHELL
# /bin/zsh
These are set by macOS before you log in. Built-ins like $HOME, $USER, $SHELL, $PATH, $PWD, $OLDPWD are always available.
Set your own:
MY_VAR='Hello World'
echo $MY_VAR
# Hello World
But: MY_VAR=... only exists in the current shell. Child processes (commands you run) won't see it.
export: for child processes
export GREETING='Welcome'
echo $GREETING
# Welcome
export VAR=value makes VAR part of the environment — visible to every command you run. Use it for:
- API keys / tokens.
- Tool-specific config (
EDITOR,PAGER,LANG). - Custom paths added to
$PATH.
Quick form:
export FOO=bar
# is equivalent to:
FOO=bar
export FOO
PATH: where commands are looked up
echo $PATH
# /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
When you type ls, Zsh searches each directory in $PATH (left to right) for an executable named ls. First match wins.
To see PATH one-per-line:
echo $PATH | tr ':' '\n'
To add ~/bin to the front (so your scripts override system commands):
export PATH="$HOME/bin:$PATH"
Add this line to ~/.zshrc to make it permanent.
which command shows where a command lives:
which ls
# /bin/ls
which python3
# /usr/local/bin/python3
Prompt customization
Zsh's prompt is set via the PROMPT variable:
echo $PROMPT
# %n@%m %1~ %#
Common escape codes:
%n— username.%m— short hostname.%~— current directory (with~for home).%#—%for users,#for root.%T— time HH:MM.%D{format}— date with custom format.%F{color}...%f— foreground color.%B...%b— bold.
Build it up:
PROMPT='%n > '
PROMPT='%n@%m > '
PROMPT='%n@%m %~ %# '
PROMPT='%F{green}%n%f@%F{blue}%m%f %F{yellow}%~%f %# '
Last line: green username, blue hostname, yellow path. The %f resets to default color.
For a multi-line prompt:
PROMPT='
%F{cyan}%~%f
%# '
RPROMPT: right-side prompt
Zsh has a right-side prompt too:
RPROMPT='%T' # shows current time on the right
Useful for time, git status, exit code, etc. Disappears when you start typing past it.
Git in the prompt
Many devs add git branch info. Manually:
function git_branch() {
git branch --show-current 2>/dev/null
}
setopt prompt_subst # allow command substitution in PROMPT
PROMPT='%n@%m %~ %F{green}$(git_branch)%f %# '
For something more polished, use a prompt theme like starship (universal across shells), powerlevel10k, or pure.
.zshrc: the persistent config
~/.zshrc runs every time you start a new Zsh session. Add aliases, exports, prompt config, function definitions — anything you want available.
Open it:
nano ~/.zshrc
A typical .zshrc might contain:
# Aliases
alias ll='ls -la'
alias gs='git status'
alias ..='cd ..'
# Environment
export EDITOR='nvim'
export PAGER='less'
export PATH="$HOME/bin:$PATH"
# Prompt
setopt prompt_subst
PROMPT='%F{green}%n%f@%F{blue}%m%f %F{yellow}%~%f %# '
# History
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt SHARE_HISTORY HIST_IGNORE_DUPS
# Completions
autoload -U compinit && compinit
After editing, reload:
source ~/.zshrc
Or just open a new terminal tab.
The startup files (in order)
When Zsh starts, it sources these files (if they exist):
/etc/zshenv— system-wide, always.~/.zshenv— per-user, always.- For login shells:
/etc/zprofile,~/.zprofile. - For interactive shells:
/etc/zshrc,~/.zshrc. - For login shells:
/etc/zlogin,~/.zlogin.
Most things go in ~/.zshrc (interactive shells). Use ~/.zshenv only for variables that must be set even in non-interactive shells (e.g., PATH for cron jobs).
Useful setopt flags
Zsh has hundreds of options. A starter set for ~/.zshrc:
setopt AUTO_CD # `cd` is implied — typing a dir name moves
setopt AUTO_PUSHD # cd builds a stack — `popd` to go back
setopt INTERACTIVE_COMMENTS # allow # comments at the prompt
setopt SHARE_HISTORY # share history across all open shells
setopt HIST_IGNORE_DUPS # don't store consecutive duplicates
setopt HIST_REDUCE_BLANKS # collapse multiple spaces
setopt EXTENDED_GLOB # **/ recursive, **/(*.txt) etc.
setopt NO_BEEP # silence the bell
setopt PROMPT_SUBST # allow $() and $vars in PROMPT
Each setopt is one line. See man zshoptions for the full list (it's massive).
Functions
For anything beyond simple substitution, write a shell function:
mkcd() {
mkdir -p "$1" && cd "$1"
}
Now mkcd projects/new creates the directory and enters it.
Lesson 11 covers functions in depth.
A starter .zshrc
# === PATH ===
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
# === Editor ===
export EDITOR='nvim'
export VISUAL='nvim'
# === Aliases ===
alias ll='ls -la'
alias la='ls -lah'
alias ..='cd ..'
alias ...='cd ../..'
alias gs='git status'
alias gd='git diff'
alias gco='git checkout'
# === History ===
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt SHARE_HISTORY HIST_IGNORE_DUPS HIST_IGNORE_SPACE
# === Options ===
setopt AUTO_CD AUTO_PUSHD EXTENDED_GLOB
setopt INTERACTIVE_COMMENTS PROMPT_SUBST
# === Prompt ===
PROMPT='%F{green}%n%f@%F{blue}%m%f %F{yellow}%~%f %# '
# === Completions ===
autoload -U compinit && compinit
Save, source ~/.zshrc, enjoy.
Common stumbles
Edits to .zshrc not taking effect. New tabs get them; the current tab needs source ~/.zshrc.
Variable used before defined. echo $X returns empty. Add set -u in scripts to error on undefined (lesson 7).
Aliasing builtins. alias cd='cd ..' recurses (Zsh actually catches this, but be careful). Use functions for anything non-trivial.
Quoting in aliases. alias greet='echo $USER' expands $USER at alias-call time (because of single quotes). alias greet="echo $USER" expands at definition time. Usually you want single quotes.
Adding to PATH twice. Each new shell sources .zshrc, which prepends. After many opens, PATH is duplicated. Guard:
case ":$PATH:" in
*":$HOME/bin:"*) ;;
*) export PATH="$HOME/bin:$PATH" ;;
esac
PROMPT with $() not working. Need setopt prompt_subst.
Slow shell startup. Too much in .zshrc. Profile with zprof:
# At top of .zshrc
zmodload zsh/zprof
# At end
zprof
What's next
Lesson 6: your first shell script. Shebangs, chmod +x, running scripts.
Recap
alias name='cmd' for shortcuts (per session). Persist by adding to ~/.zshrc. export VAR=value for child-process visibility; plain VAR=value is current-shell only. PATH controls where commands are searched. PROMPT='...' with %n %m %~ %# %F{color}%f customizes. Reload via source ~/.zshrc. setopt toggles useful options (AUTO_CD, SHARE_HISTORY, EXTENDED_GLOB).
Next lesson: writing shell scripts.