I spend most of my time on my laptop in 3 apps, a browser, a code editor and a terminal emulator, so I want things to look pretty and be useful in my terminal. After a lot of tweaking, I think I finally have a decent terminal configuration using Zsh which I’ll share in this article.
Zsh is an interactive shell similar to bash (the default shell on Mac OS and Linux systems). What’s great about Zsh are the features such as autocompletion and the customization options available. For someone like me who forgets and mistypes commands all the time, autocompletion is a life-saver. It’s because of autocompleting that nobody knows how terrible I am at coding.
Switching to Zsh
Zsh is installed in the default installation on Mac OS but installing via brew ensure that we have the latest available features and bug fixes. To install Zsh on Mac OS, run the following if you already have Homebrew:
1 |
brew install zsh |
For other Operating Systems, it should be available to install from the default repositories.
To change the default shell to Zsh, you can use the chsh
command, However, you need to add Zsh your shells list first:
1 |
grep -q -f $(which zsh) /etc/shells || echo $(which zsh) | sudo tee -a /etc/shells |
Then run the following to switch the shell to Zsh:
1 |
chsh -s $(which zsh) |
I’ve spent a lot of time configuring Zsh to have a great autocompletion experience. Sure, I could use the Fish shell instead, but Zsh’s syntax is much closer to bash which is a big reason I prefer Zsh.
When I searched the web on how to configure Zsh, most of the guides I found used Oh My Zsh. While it provides a great set of default configuration and plugins, it’s so bloated that it’ll reduce the startup time of the shell by several seconds. I was using it for a while, but I don’t think I even used 1% of what it has, all I wanted was a good autocompletion and few other niceties. It was unreasonable to use Oh My Zsh to spend seconds staring at the terminal while it starts up every time.

So I decided to switch to antibody to manage my Zsh plugins. While it’s not as feature-rich as Oh My Zsh and I will need to configure things manually, it’s much faster, and while it took a fair amount of time to tweak the config, the actual amount of config needed isn’t that much. The speedup was totally worth it.
Ok, enough blabbering. Now, let’s configure our shell. Note that I didn’t cover any aliases or plugins providing aliases here since they are not specific to Zsh and aliases are more like personal preferences. All of the code I have mentioned below need to be added to the ~/.zshrc
file.
Enabling autocompletion
The first thing we want is autocompletion. To initialize the completion system, we need to load compinit
. However, loading the completions every time can slow down the startup time. Fortunately, we can use a cache for faster startup. To load compinit
, let’s add the following to the ~/.zshrc
file:
1 2 3 4 5 6 7 |
autoload -Uz compinit typeset -i updated_at=$(date +'%j' -r ~/.zcompdump 2>/dev/null || stat -f '%Sm' -t '%j' ~/.zcompdump 2>/dev/null) if [ $(date +'%j') != $updated_at ]; then compinit -i else compinit -C -i fi |
Next thing we need to load is the complist
module. It provides a menu list from where we can highlight and select completion results. To load it, we just need to add the following:
1 |
zmodload -i zsh/complist |
Configuring Zsh
By default, Zsh doesn’t save the history to the disk for the commands we have typed. Since the history is often useful and can be used to provide autosuggestions, we would want to save the history. To do it, we just need to specify the number of entries to store and a file name where to save the history:
1 2 3 |
HISTFILE=$HOME/.zsh_history HISTSIZE=100000 SAVEHIST=$HISTSIZE |
We might also want to configure some more options to improve how the history is saved, for example, eliminating duplicate entries from the history:
1 2 3 4 |
setopt hist_ignore_all_dups # remove older duplicate entries from history setopt hist_reduce_blanks # remove superfluous blanks from history items setopt inc_append_history # save history entries as soon as they are entered setopt share_history # share history between different instances of the shell |
A cool feature in Zsh is the ability to navigate to a directory without typing cd
. For example, if we type projects
and there’s directory named Projects
, Zsh can automatically cd
to the directory unless projects
is a command available in the path. I use this feature all the time and miss it a lot when I ssh to a server where I don’t have it. To enable it, we can do the following:
1 |
setopt auto_cd # cd by typing directory name if it's not a command |
I make typos all the time. Zsh can autocorrect those typos and ask us to run the correct command when we try to run a wrong command. To enable it, we can do the following:
1 |
setopt correct_all # autocorrect commands |
When we type a command and press the tab key, Zsh shows a list of completion items. We can tweak few things to improve the autocomplete menu:
1 2 3 |
setopt auto_list # automatically list choices on ambiguous completion setopt auto_menu # automatically use menu completion setopt always_to_end # move cursor to end if word had one match |
We can also tweak the UX of the autocompletion menu to match even if we made a typo and enable navigation using the arrow keys:
1 2 3 |
zstyle ':completion:*' menu select # select completions with arrow keys zstyle ':completion:*' group-name '' # group results by category zstyle ':completion:::::' completer _expand _complete _ignored _approximate # enable approximate matches for completion |
Now we get autocompletion with a list of suggestions from where we can select the options with the tab key or the arrow key:

One issue with Zsh is that the delete key doesn’t work as expected and inputs ~
instead. To workaround this, we need to add the following keybindings:
1 2 |
bindkey '^[[3~' delete-char bindkey '^[3;5~' delete-char |
Credits: https://blog.pilif.me/2004/10/21/delete-key-in-zsh/
Need to conduct React Native training in your company?
Talk to us!Plugins
The next step is to install some plugins to improve the experience even more. To install the plugins, we are going to use antibody. You can follow the installation instructions on the website. I use the following plugins:
zdharma/fast-syntax-highlighting
This plugin provides syntax highlighting for the commands we type in the Terminal. In addition, when we’re typing a command, it’ll be highlighted in red if it’s invalid and in green, if it’s valid.

This plugin is also needed by the zsh-users/autosuggestions
plugin which we’ll install next.
To use it, add the following to your ~/.zshrc
1 |
antibody bundle zdharma/fast-syntax-highlighting |
zsh-users/zsh-autosuggestions
This plugin will suggest commands as you type, which you can select with the right arrow key (➡). The suggestions are based on the history.

To use it, add the following to your ~/.zshrc
1 |
antibody bundle zsh-users/zsh-autosuggestions |
zsh-users/zsh-history-substring-search
This plugin allows you to type part of a command which exists in the history, and then select matching commands with specific keys, for example, to select with up and down arrow keys, we need to add the following configuration:
1 2 |
bindkey '^[[A' history-substring-search-up bindkey '^[[B' history-substring-search-down |

To use it, add the following to your ~/.zshrc
1 |
antibody bundle zsh-users/zsh-history-substring-search |
zsh-users/zsh-completions
This plugin provides additional completions not available in Zsh yet.
To use it, add the following to your ~/.zshrc
1 |
antibody bundle zsh-users/zsh-completions |
marzocchi/zsh-notify
This plugin shows a desktop notification when a command takes a long time to finish or fails if the Terminal tab is inactive. Unfortunately, it doesn’t work with Hyper on Mac OS which I use. But it should work if you use one of the supported Terminals or you’re on Linux.
To use it, add the following to your ~/.zshrc
1 |
antibody bundle marzocchi/zsh-notify |
If you’re on Mac OS, you also need to install terminal-notifier
1 |
brew install terminal-notifier |
buonomo/yarn-completion
Being a web developer, I work with yarn every day. This plugin provides autocompletion for yarn commands and scripts, which is a huge help. In addition, we can see all the available scripts without having to open the package.json
file directly in the terminal.

To use it, add the following to your ~/.zshrc
1 |
antibody bundle buonomo/yarn-completion |
Customizing the prompt
I like a minimal prompt with a few info such as the current directory and git metadata. But if you prefer a different style, there are tons of prompts available on the internet.
For my setup, I used the spaceship prompt with the following setup:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
SPACESHIP_PROMPT_ORDER=( user # Username section dir # Current directory section host # Hostname section git # Git section (git_branch + git_status) hg # Mercurial section (hg_branch + hg_status) exec_time # Execution time line_sep # Line break vi_mode # Vi-mode indicator jobs # Background jobs indicator exit_code # Exit code section char # Prompt character ) SPACESHIP_PROMPT_ADD_NEWLINE=false SPACESHIP_CHAR_SYMBOL="❯" SPACESHIP_CHAR_SUFFIX=" " # Simplify prompt if we're using Hyper if [[ "$TERM_PROGRAM" == "Hyper" ]]; then SPACESHIP_PROMPT_SEPARATE_LINE=false SPACESHIP_DIR_SHOW=false SPACESHIP_GIT_BRANCH_SHOW=false fi antibody bundle denysdovhan/spaceship-prompt |
1 |
antibody bundle denysdovhan/spaceship-prompt |
Since I already have the folder and git information in the status bar in Hyper, I conditionally removed them from the prompt when using Hyper.
Note that you’ll need to install and configure powerline fonts to be able to display additional symbols in the spaceship prompt. Personally, I use Fira Code which supports these symbols.
Another similar prompt is pure, but I didn’t use it because of the lack of customisability.
Open new tabs in same directory
If you’re using Terminal.app on Mac OS, opening a new tab doesn’t preserve the current working directory when using Zsh. To make it work, we need to add the following:
1 2 3 4 5 6 |
if [[ "$TERM_PROGRAM" == "Apple_Terminal" ]]; then function chpwd { printf 'e]7;%sa' "file://$HOSTNAME${PWD// /%20}" } chpwd fi |
Wrapping up
To make it easier to copy/paste, here is the complete configuration with all above tweaks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# Enable autocompletions autoload -Uz compinit typeset -i updated_at=$(date +'%j' -r ~/.zcompdump 2>/dev/null || stat -f '%Sm' -t '%j' ~/.zcompdump 2>/dev/null) if [ $(date +'%j') != $updated_at ]; then compinit -i else compinit -C -i fi zmodload -i zsh/complist # Save history so we get auto suggestions HISTFILE=$HOME/.zsh_history HISTSIZE=100000 SAVEHIST=$HISTSIZE # Options setopt auto_cd # cd by typing directory name if it's not a command setopt auto_list # automatically list choices on ambiguous completion setopt auto_menu # automatically use menu completion setopt always_to_end # move cursor to end if word had one match setopt hist_ignore_all_dups # remove older duplicate entries from history setopt hist_reduce_blanks # remove superfluous blanks from history items setopt inc_append_history # save history entries as soon as they are entered setopt share_history # share history between different instances setopt correct_all # autocorrect commands setopt interactive_comments # allow comments in interactive shells # Improve autocompletion style zstyle ':completion:*' menu select # select completions with arrow keys zstyle ':completion:*' group-name '' # group results by category zstyle ':completion:::::' completer _expand _complete _ignored _approximate # enable approximate matches for completion # Load antibody plugin manager source <(antibody init) # Plugins antibody bundle zdharma/fast-syntax-highlighting antibody bundle zsh-users/zsh-autosuggestions antibody bundle zsh-users/zsh-history-substring-search antibody bundle zsh-users/zsh-completions antibody bundle marzocchi/zsh-notify antibody bundle buonomo/yarn-completion # Keybindings bindkey '^[[A' history-substring-search-up bindkey '^[[B' history-substring-search-down bindkey '^[[3~' delete-char bindkey '^[3;5~' delete-char # Theme SPACESHIP_PROMPT_ORDER=( user # Username section dir # Current directory section host # Hostname section git # Git section (git_branch + git_status) hg # Mercurial section (hg_branch + hg_status) exec_time # Execution time line_sep # Line break vi_mode # Vi-mode indicator jobs # Background jobs indicator exit_code # Exit code section char # Prompt character ) SPACESHIP_PROMPT_ADD_NEWLINE=false SPACESHIP_CHAR_SYMBOL="❯" SPACESHIP_CHAR_SUFFIX=" " # Simplify prompt if we're using Hyper if [[ "$TERM_PROGRAM" == "Hyper" ]]; then SPACESHIP_PROMPT_SEPARATE_LINE=false SPACESHIP_DIR_SHOW=false SPACESHIP_GIT_BRANCH_SHOW=false fi antibody bundle denysdovhan/spaceship-prompt # Open new tabs in same directory if [[ "$TERM_PROGRAM" == "Apple_Terminal" ]]; then function chpwd { printf 'e]7;%sa' "file://$HOSTNAME${PWD// /%20}" } chpwd fi |
You can copy the whole config and paste it to your ~/.zshrc
file after installing antibody
and it should work without any changes. Let me know if you have any questions and I’ll try to answer them. Note that I’m no expert at this and might be missing crucial config options. Most guides on the internet use Oh My Zsh or don’t explain much, so I was left to figure things out by reading various configs. If I’m missing something, please leave a comment and I’ll try to update the guide.
Also, if you’re curious, I have used Hyper with the hyperminimal plugin and the hyperterm-palenight theme in the screenshots above.
Satya