Back to Blog

Zsh Configuration Tutorial: Customize Your Terminal with .zshrc, Aliases & Prompts

Sandy LaneSandy Lane

Video: Zsh Configuration Tutorial: Customize Your Terminal with .zshrc, Aliases & Prompts by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Zsh Lesson 5: Customizing Zsh — Aliases, Env Vars, PROMPT, .zshrc

alias ll='ls -la' for shortcuts. export FOO=bar for child processes. PROMPT='%n@%m %~ %# ' customizes the prompt. Put everything in ~/.zshrc to persist. source ~/.zshrc reloads.

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):

  1. /etc/zshenv — system-wide, always.
  2. ~/.zshenv — per-user, always.
  3. For login shells: /etc/zprofile, ~/.zprofile.
  4. For interactive shells: /etc/zshrc, ~/.zshrc.
  5. 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.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.