Setting the terminal title in zsh
By default, zsh doesn’t set the terminal title, making it inefficient to know which application is running in each tab, or when a long-running application has exited, as each tab must be visited in turn.
Outside the shell, some terminal emulators provide functionality to set the title, typically formatting a string containing one or more of your username, hostname, current working directory, and any currently running application. Such terminal emulators include Kitty and Ghostty (Ghostty’s shell integration originated as a fork of Kitty’s).
However, even if you are using such a terminal emulator, the format is unlikely to be optimal for your circumstances. If you frequently connect to other hosts via SSH, including the hostname is valuable. However, if you rarely or never use SSH, including the hostname confers no distinct information and takes up space.
As such, whether your terminal emulator already sets the title or not, you can benefit from configuring a custom format. Several zsh features, especially prompt sequence expansion and hooks, compose to make this easy.
Almost all popular terminal emulators for Linux or macOS support xterm’s escape sequence for setting the title. This is formed of the escape character \e]0;
(or \033
in octal), followed by the title contents, and terminated by the bell character \a
. To test support in your current terminal emulator, run:
In addition to the printf
built-in, which is defined by POSIX, zsh provides a print
built-in, which is specific to zsh and offers extended functionality.
print
has a -P
flag, which enables expansion of the prompt sequences you may already be familiar with if you’ve customised your zsh prompt. A full listing of available prompt sequences is provided in the zsh documentation, but some frequently used examples are:
%n
: the username of the current user (as returned by the shell, equal to the value of$USER
in most environments).%m
: the hostname of the current machine, up to the first.
.%~
: the current working directory, with the value of$HOME
replaced with~
if it’s a subdirectory of$HOME
.
So, to set the title to <username>@<hostname>: <cwd>
run:
(the -n
flag suppresses printing a trailing newline character).
However, if you now move to a different directory with cd
, the title will show the wrong directory until you run the print
command again. You can keep the title synchronised with the current state of the shell using zsh’s hooks.
Zsh’s hooks allow registering functions to be called each time a specific event occurs, such as after the working directory changes. The two most useful events for setting the title are precmd
, triggered before each new prompt is drawn, and preexec
, triggered before a command is executed.
The add-zsh-hook
function allows adding a function to be triggered on a given event, and must first be loaded with:
You can then add a function which calls print
to set the title each time a new prompt is drawn, by adding a hook to the precmd
event:
{
)
}
In contrast to precmd
hooks, preexec
hooks are run after a command line has been entered, but before it is executed, and so allow the title to include the name of the currently running command. Functions registered for preexec
are called with positional arguments, the second of which is the command line being executed. Therefore, you can use print
within a preexec
hook to set the title to the same value as in the precmd
hook, but with the currently running command appended:
{
)
}
When a command exits, a new prompt will be drawn, triggering the precmd
hook again, which will set the title back to not include a command name.
To use these hooks, make sure to disable title setting in your terminal emulator; otherwise, your own hooks will be overridden. For example, set shell_integration no-title
in the configuration file for Kitty, and shell-integration-features = no-title
in the configuration file for Ghostty.
Bringing this all together, you can set the title to show the username, hostname, working directory, and current command (when one is running), and keep the title up to date, by adding the following to ~/.zshrc
:
{
)
}
{
)
}
You can further customise this using conditional checks in the precmd
or preexec
hooks, allowing you to display information like the hostname only when connected to an SSH session, or show the username based on a specific condition, such as it being root
. For example, to show the username and hostname only if you’re connected via SSH, branch on $SSH_CONNECTION
being set:
if ; then
else
fi
Think about what other contextual information would be useful to you, and try it out!