Simple Zsh Theme Guide Cheat sheet Open the editor

Zsh prompt escape codes cheat sheet

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.

On this page

Who and where

EscapeExpands toExample
%nUsername ($USERNAME)user
%mHostname up to the first dotworkstation
%MFull hostnameworkstation.local
%2mHostname, first 2 componentsworkstation.local
%lTerminal line, without /dev/ttypts/3
%yTerminal line, without /dev/pts/3
%## when running as root, otherwise %%

Directory

EscapeExpands toExample
%~Working directory, home collapsed to ~~/code/my-project
%dWorking directory, absolute/home/user/code/my-project
%1~Last 1 component of %~my-project
%2~Last 2 components of %~code/my-project
%c %C %.Trailing component, like %1~ (%C never shortens to ~)my-project
%2cLast 2 componentscode/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

EscapeExpands to
%?Exit status of the last command
%jNumber of background jobs
%h %!Current history event number
%L$SHLVL — how deep you have nested shells
%iLine number being executed (useful in PROMPT4)
%_Parser state, e.g. for or quote (PROMPT2)
%vFirst element of $psvar; %3v for the third

Date and time

EscapeExpands toExample
%T24-hour time, hours and minutes9:41
%*24-hour time with seconds9:41:07
%t %@12-hour time, am/pm 9:41AM
%wDay as day-ddTue-21
%WDate as mm/dd/yy10/21/26
%DDate as yy-mm-dd26-10-21
%D{%H:%M:%S}Anything strftime understands09: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

EscapeMeaning
%F{red} %fForeground by name: black, red, green, yellow, blue, magenta, cyan, white (palette slots 0–7)
%F{166} %fForeground by xterm-256 index; 16–255 are identical in every terminal
%F{#ff8700} %fForeground as 24-bit hex, where truecolor is supported
%K{…} %kBackground, same three forms
%B %bBold on / off
%U %uUnderline on / off
%S %sReverse video (standout) on / off
%EClear 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).

TestTrue when
!The shell is running as root
#The effective UID equals n
?The last exit status equals n (default 0)
jThere are at least n background jobs
L$SHLVL is at least n
v$psvar has at least n elements
VElement n of $psvar is set and non-empty
C / ~ / / The path has at least n components
lAt least n characters have been printed on this line
eThe evaluation depth is at least n
SThe 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

FormMeaning
%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

FormMeaning
%%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.
%GInside %{…%}, 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) …'.

FunctionPrints
git_prompt_infoThe branch (or short sha when detached) between PREFIX and SUFFIX, with DIRTY or CLEAN appended
git_prompt_statusOne marker per kind of change: added, modified, deleted, renamed, unmerged, untracked, stashed
git_prompt_short_sha / git_prompt_long_shaThe commit, wrapped in SHA_BEFORE / SHA_AFTER
git_remote_statusAhead / behind / diverged against the upstream branch
git_prompt_ahead / git_prompt_behindThe commit counts on their own
parse_git_dirtyDIRTY or CLEAN; this is the call that costs time in a big repository
git_current_branchThe branch name, unwrapped — handy inside your own functions
virtualenv_prompt_infoThe active virtualenv (needs the virtualenv plugin)
ruby_prompt_info / nvm_prompt_infoThe active Ruby / Node version (needs the matching plugin)
battery_pct_promptBattery percentage (needs the battery plugin)

ZSH_THEME_GIT_PROMPT variables

Each is a plain string, so colour escapes go inside it.

VariableUsed by
ZSH_THEME_GIT_PROMPT_PREFIXOpens git_prompt_info
ZSH_THEME_GIT_PROMPT_SUFFIXCloses it
ZSH_THEME_GIT_PROMPT_DIRTYAppended when the tree has changes
ZSH_THEME_GIT_PROMPT_CLEANAppended when it does not
ZSH_THEME_GIT_PROMPT_ADDEDgit_prompt_status, one marker each
ZSH_THEME_GIT_PROMPT_MODIFIED
ZSH_THEME_GIT_PROMPT_DELETED
ZSH_THEME_GIT_PROMPT_RENAMED
ZSH_THEME_GIT_PROMPT_UNMERGED
ZSH_THEME_GIT_PROMPT_UNTRACKED
ZSH_THEME_GIT_PROMPT_STASHED
ZSH_THEME_GIT_PROMPT_AHEAD / _BEHIND / _DIVERGEDPosition against the upstream branch
ZSH_THEME_GIT_PROMPT_SHA_BEFORE / _SHA_AFTERWrap the commit sha
ZSH_THEME_GIT_PROMPT_REMOTE_STATUS_DETAILEDSet to 1 to make git_remote_status print counts
ZSH_THEME_GIT_PROMPT_EQUAL_REMOTE / _AHEAD_REMOTE / _BEHIND_REMOTE / _DIVERGED_REMOTEMarkers for the detailed form
ZSH_THEME_VIRTUALENV_PREFIX / _SUFFIXWrap virtualenv_prompt_info
ZSH_THEME_RUBY_PROMPT_PREFIX / _SUFFIXWrap ruby_prompt_info

Hooks and prompt variables

NameWhat it does
PROMPT / PS1The left prompt
RPROMPT / RPS1The right prompt, erased when a line is accepted
PROMPT2Continuation prompt
PROMPT4Trace prompt, under set -x
setopt prompt_substExpands $(…) and ${…} on every prompt — required for git info
setopt transient_rpromptErases the right prompt on older zsh versions that do not by default
add-zsh-hook precmd fnRuns fn before each prompt — where timers, psvar and window titles belong
add-zsh-hook preexec fnRuns fn after you press enter, with the command line as $1
psvarAn 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.