Every escape zsh expands inside PROMPT and RPROMPT, plus the
oh-my-zsh helpers and ZSH_THEME_GIT_PROMPT_* variables that themes are built
from. The examples are rendered against a shell where the user is
user on workstation, in
/home/user/code/my-project, on branch main.
Trailing component, like %1~ (%C never shortens to ~)
my-project
%2c
Last 2 components
code/my-project
A leading slash survives.%3~ of
/usr/share/zsh is /usr/share/zsh — zsh keeps the root slash when
nothing was cut — while %2~ is share/zsh. Themes that pad or
align on path length have to account for that one character.
Shell status
Escape
Expands to
%?
Exit status of the last command
%j
Number of background jobs
%h %!
Current history event number
%L
$SHLVL — how deep you have nested shells
%i
Line number being executed (useful in PROMPT4)
%_
Parser state, e.g. for or quote (PROMPT2)
%v
First element of $psvar; %3v for the third
Date and time
Escape
Expands to
Example
%T
24-hour time, hours and minutes
9:41
%*
24-hour time with seconds
9:41:07
%t %@
12-hour time, am/pm
9:41AM
%w
Day as day-dd
Tue-21
%W
Date as mm/dd/yy
10/21/26
%D
Date as yy-mm-dd
26-10-21
%D{%H:%M:%S}
Anything strftime understands
09:41:07
%T and %* do not pad the hour. At nine in the
morning they give 9:41, not 09:41, so a right prompt built on them
shifts by a column for half the day. Use %D{%H:%M} when you want a fixed width.
Colour and style
Escape
Meaning
%F{red} %f
Foreground by name: black, red, green, yellow, blue, magenta, cyan, white (palette slots 0–7)
%F{166} %f
Foreground by xterm-256 index; 16–255 are identical in every terminal
%F{#ff8700} %f
Foreground as 24-bit hex, where truecolor is supported
%K{…} %k
Background, same three forms
%B %b
Bold on / off
%U %u
Underline on / off
%S %s
Reverse video (standout) on / off
%E
Clear to the end of the line
There is no escape for italic or dim; those need raw SGR sequences wrapped in
%{…%}, such as %{$'\e[3m'%}.
Conditionals
%(x.true.false) — x is a test letter, optionally preceded by a
number, and the character after it is the separator, so any character that does not appear
in the branches will do: %(?,ok,fail) is the same test as
%(?.ok.fail).
Test
True when
!
The shell is running as root
#
The effective UID equals n
?
The last exit status equals n (default 0)
j
There are at least n background jobs
L
$SHLVL is at least n
v
$psvar has at least n elements
V
Element n of $psvar is set and non-empty
C / ~ / /
The path has at least n components
l
At least n characters have been printed on this line
e
The evaluation depth is at least n
S
The seconds of the current time equal n; D, d, w, T, t test month, day, weekday, hour and minute
PROMPT='%(?.%F{green}➜.%F{red}➜ %?)%f ' # arrow goes red and shows the code on failure
PROMPT='%(1j.%F{yellow}⚙%j %f.)' # only when jobs are running
PROMPT='%(!.%F{red}root%f.%F{green}%n%f) ' # root in red, everyone else in green
Truncation
Form
Meaning
%10<…<%~%<<
Truncate the enclosed text to 10 columns, cutting on the left and marking the cut with …
%10>…>%~%>>
The same, cutting on the right
%-10<<…%<<
A negative width leaves 10 columns free for the rest of the prompt
The marker counts towards the width, so %10<…< leaves nine columns of
text. Truncation is the only reliable way to keep a prompt from wrapping on a narrow
terminal; the editor shows the result at any column count.
Literals and width
Form
Meaning
%%
A literal percent sign
%)
A literal ), needed inside a conditional branch
%{…%}
Zero-width: the enclosed bytes move the cursor nowhere as far as zsh is concerned. Wrap raw terminal escapes in this — and nothing else.
%1{…%}
The same, but the content is counted as 1 column. This is how a Powerline glyph or an emoji keeps the line aligned.
%G
Inside %{…%}, counts one more column of output
Never wrap %F or %B in %{…%}. zsh
already knows they take no space. Wrapping them is harmless in the simple case and wrong as
soon as the content inside is not purely escapes — which is how most “my prompt smears when
I edit a long command” reports begin.
oh-my-zsh prompt helpers
These are shell functions, so they need setopt prompt_subst and single quotes:
PROMPT='… $(git_prompt_info) …'.
Function
Prints
git_prompt_info
The branch (or short sha when detached) between PREFIX and SUFFIX, with DIRTY or CLEAN appended
git_prompt_status
One marker per kind of change: added, modified, deleted, renamed, unmerged, untracked, stashed
git_prompt_short_sha / git_prompt_long_sha
The commit, wrapped in SHA_BEFORE / SHA_AFTER
git_remote_status
Ahead / behind / diverged against the upstream branch
git_prompt_ahead / git_prompt_behind
The commit counts on their own
parse_git_dirty
DIRTY or CLEAN; this is the call that costs time in a big repository
git_current_branch
The branch name, unwrapped — handy inside your own functions
virtualenv_prompt_info
The active virtualenv (needs the virtualenv plugin)
ruby_prompt_info / nvm_prompt_info
The active Ruby / Node version (needs the matching plugin)
battery_pct_prompt
Battery percentage (needs the battery plugin)
ZSH_THEME_GIT_PROMPT variables
Each is a plain string, so colour escapes go inside it.
Expands $(…) and ${…} on every prompt — required for git info
setopt transient_rprompt
Erases the right prompt on older zsh versions that do not by default
add-zsh-hook precmd fn
Runs fn before each prompt — where timers, psvar and window titles belong
add-zsh-hook preexec fn
Runs fn after you press enter, with the command line as $1
psvar
An array a precmd hook fills, read back as %1v and tested with %(1V.….…)
Try it
The zsh theme editor turns all of this into drag-and-drop segments and shows
the generated escapes next to a live terminal, so you can read the file it writes against
the table above. The guide to writing an oh-my-zsh theme explains how
the pieces fit together.