Hello and welcome to my first blog post (we’re not counting posts further than 15 years in the past 😉). As a software developer I spend a considerable amount of time on the command-line in my terminal emulator. In this post we are going to take the plain old default terminal setup you get with macOS to a beautiful and more functional terminal setup. We’re assuming our starting point is a fresh install of macOS Sonoma.

Enough prolog, let’s dive right into it!

Accompanying YouTube Video

I made a YouTube video in which I take you through all the steps of this blog post and also demonstrate some of the cool functionality you get. Feel free to watch it and leave me a comment, if you want to.

Installing Homebrew

To install the Terminal app, the font and some other tools, we’re going to use a package manager. Homebrew is a very popular package manager for macOS. Installation is very easy. We just open a terminal emulator and paste the command below into it and start the installation by pressing “return”. macOS comes with a pre-installed terminal emulator, that listens to the really simple but extremely fitting name: “Terminal.app”.

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

We’ll be prompted for our password during installation. Don’t worry, if you see nothing happening, while typing in your password. It’s very common on the command-line to not show any kind of feedback, during password dialogs. Also, we will have to confirm the installation of the “Xcode Command Line Tools”, if you haven’t installed them already. Depending on the speed of your computer and your internet connection, this can take a few (or many) minutes.

To add Homebrew to your PATH, every time you startup your shell we do the following:

echo >> "$HOME/.zprofile"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> "$HOME/.zprofile"
eval "$(/opt/homebrew/bin/brew shellenv)"

Now we are ready to run Homebrew’s command-line interface brew.

After the installation is finished, the first thing I always do, is to turn of Homebrew’s analytics:

brew analytics off

You can of course leave them enabled, if you want to.

At this point, we should have a fully functioning Homebrew installation. Yay, congratulations! From now on, we can easily install all kinds of software using the brew command. And that’s exactly what we’re going to do to level up our terminal setup.

Installing WezTerm

That’s where the foundation of our command-line setup comes into play: a better terminal emulator. There are quite a few possible candidates: the tried and true iTerm2 or some more modern and very popular alternatives like Alacritty or kitty. There’s also the new kid on the block: Ghostty written in Zig. It is developed by Mitchell Hashimoto, a co-founder of HashiCorp. I really want to try this out, but as the time of writing Ghostty is still in private beta and unfortunately I’ didn’t score an invite, yet. We’ll probably take it for a spin in the near future. Stay tuned for it!

But there’s no need to be upset. We will use the awesome WezTerm as our new terminal emulator. WezTerm is a fast, GPU-accelerated powerful cross-platform terminal emulator that runs on macOS, but also on Linux, FreeBSD and even Windows. It is written in Rust and boasts cool and super-useful features like a built-in terminal multiplexer, support for ligatures, a searchable scrollback just to name a few. It even supports multiple image protocols. It features relatively sane keybindings out of the box, is highly configurable via a Lua configuration file and even offers hot reloading of config changes. It’s a great piece of kit!

Enough praise for WezTerm. We’re going to put Homebrew to good use for the first time now and install WezTerm right from our current terminal. Run the following command:

brew install --cask wezterm

After the installation is finished we can finally close Terminal.app and start our shiny new terminal emulator. So let’s launch WezTerm to go on with the setup.

As a security measurement macOS wants us to confirm, that we really want to open WezTerm. We sure do, and so we confidently click on the “Open” button. Time to enjoy using your new terminal emulator!

You might think: “This doesn’t look much better than Terminal.app after all…” and I would totally agree with you. But WezTerm is very customizable. Let’s start configuring it. But first we are going to install the font we want to use in our setup.

Installing JetBrains Mono Nerd Font

Let’s leverage Homebrew again. A quick brew install --cask font-jetbrains-mono-nerd-font and we’re ready to use our newly installed font.

Configuring WezTerm

WezTerm is configured using a Lua configuration File. We’ll create this file at ~/.config/wezterm/wezterm.lua. Here’s what I have in my config with some comments for further explanation:

-- Helper function:
-- returns color scheme dependant on operating system theme setting (dark/light)
local function color_scheme_for_appearance(appearance)
  if appearance:find "Dark" then
    return "Tokyo Night"
  else
    return "Tokyo Night Light (Gogh)"
  end
end

-- Pull in WezTerm API
local wezterm = require 'wezterm'

-- Initialize actual config
local config = {}
if wezterm.config_builder then
  config = wezterm.config_builder()
end

-- Appearance
config.font = wezterm.font 'JetBrainsMono Nerd Font'
config.font_size = 14.0
config.color_scheme = color_scheme_for_appearance(wezterm.gui.get_appearance())
config.window_decorations = "RESIZE"
config.hide_tab_bar_if_only_one_tab = true
config.native_macos_fullscreen_mode = false

-- Keybindings
config.keys = {
  -- Default QuickSelect keybind (CTRL-SHIFT-Space) gets captured by something
  -- else on my system
  {
    key = 'A',
    mods = 'CTRL|SHIFT',
    action = wezterm.action.QuickSelect,
  },
}

-- Return config to WezTerm
return config

This config is relatively small, because I like most of WezTerm’s default keybindings and settings. For those shiny icons in the terminal prompt, we use JetBrains Mono in the patched Nerd Font version, which we installed before with font size 14. This is currently my favorite monospace font. You can of course install any other Nerd Font and use that, if you want to. A quick

brew search --cask nerd

will give you a list of installable fonts that you can easily install with the brew command.

I just have set up the beautiful color scheme Tokyo Night and it’s light counterpart, respectively. Configured like this, WezTerm dynamically switches between the light and dark themes, depending on the theme setting on the operating system level. So if I switch between Dark and Light in macOS’ Appearance settings, Wezterm’s theme automatically switches, too. I often use this feature during changing lighting conditions. When it’s really bright and the sun is shining, I switch to Light mode and when it gets a little bit more gloomy, I prefer the Dark setting.

We remove the default window decorations but are keeping the window resizable with the value RESIZE for the window_decorations setting and hide the tab bar, if we only have one tab open. Also we don’t want to use macOS’ native fullscreen mode.

WezTerm in Action

I want to show you a few of WezTerm’s features. I use these all the time and find them very useful.

Splitting Windows

WezTerm enables you to split your terminal windows. When you open a new WezTerm window it contains exactly one pane, that takes up all of the available space inside the window. You can then split this pane into smaller panes, either horizontally or vertically. These newly created panes can then be split times and times again. WezTerm’s default keybinds for splitting are the following:

  • ALT-CTRL-SHIFT-": Splits pane vertical
  • ALT-CTRL-SHIFT-%: Splits pane horizontal
  • CTRL-SHIFT-Z: Toggles pane’s zoom state, which means you can toggle the pane between taking up the complete window or just being it’s actual size
  • CTRL-SHIFT-LeftArrow/DownArrow/UpArrow/RightArrow: Switches focus to from currently active pane to pane in given direction
  • ALT-CTRL-SHIFT-LeftArrow/DownArrow/UpArrow/RightArrow: Resizes the currently active pane in the given direction

Quick Select Mode

Quick Select mode let’s you quickly highlight text in your terminal, that matches commonly copied patterns and prefixes them with a prefix of one or two characters. Typing these characters copies the corresponding text into your clipboard.

To see how this looks in practice, I encourage you to take a look at the YouTube video I made related to this blog post or visit WezTerm’s official documentation on Quick Select Mode. A quick summary of the default shortcuts:

  • CTRL-SHIFT-Space: Activate Quick Select Mode (I’ve bound this to CTRL-SHIFT-A in our config file earlier).
  • character-prefix: Copy corresponding highlighted pattern
  • ESC: Deactivate Quick Select Mode

Copy Mode

Copy mode enables you to select and copy text to the clipboard all while just using your keyboard. Some relevant default keybindings are:

  • CTRL-SHIFT-X Activate CopyMode in CopyMode Esc exits CopyMode
  • h, j, k, l or LeftArrow,DownArrow,UpArrow, RightArrow, to move around by character
  • w, e, b to move by word
  • Other Vim motion keybinds to move around like CTRL-U or CTRL-D
  • Toggle selection modes, like visual modes in vim v, SHIFT-V, CTRL-V,
  • Move to other ends of selection: o, SHIFT-O
  • y to copy to system clipboard

The full list of default keybinds can be found in WezTerm’s official documentation on Copy Mode or you can of course create your own keybinds.

Installing Zsh Plugins Using Homebrew

We’ll just install two plugins, that I think are essential. These are zsh-autosuggestions and zsh-syntax-highlighting. We’re not even going to use a special plugin manager for Zsh. Instead we’re using Homebrew again to install the plugins:

brew install zsh-autosuggestions zsh-syntax-highlighting

Then we’ll add the following lines to the end of our .zshrc:

source /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh
source /opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

After that we have to restart WezTerm or we can manually source ~/.zshrc to activate the plugins.

[!NOTE] If you receive the “highlighters directory not found” error message, you may need to add the following to your ~/.zshenv:

export ZSH_HIGHLIGHT_HIGHLIGHTERS_DIR=/opt/homebrew/share/zsh-syntax-highlighting/highlighters

Installing Starship

To enhance our terminal experience even further, we need a better prompt. Starship is a really fast and very customizable shell prompt, that works on most common shells and operating systems. It is written in Rust and comes with a nice default configuration. To install it we once again use our trusty friend Homebrew and cast the following spell:

brew install starship

After installing Starship, we need to add following line to the end of our .zshrc:

eval "$(starship init zsh)"

Now just restart your Terminal and you have your new prompt! To see Starship in action, I shamelessly point you to the related YouTube video I made again.

Installing eza

To finish up for today, we are going to replace the good old ls command with a modern alternative called eza. You probably know the drill by now:

brew install eza

That’s all you really have to do, to now use eza. I like to add a few aliases to my .zshrc, to keep using the muscle memory I built up over the years and effortlessly use of the icons and built-in tree-functionality:

# eza (better `ls`)
alias l="eza --icons"
alias ls="eza --icons"
alias ll="eza -lg --icons"
alias la="eza -lag --icons"
alias lt="eza -lTg --icons"
alias lt1="eza -lTg --level=1 --icons"
alias lt2="eza -lTg --level=2 --icons"
alias lt3="eza -lTg --level=3 --icons"
alias lta="eza -lTag --icons"
alias lta1="eza -lTag --level=1 --icons"
alias lta2="eza -lTag --level=2 --icons"
alias lta3="eza -lTag --level=3 --icons"

Where To Go From Here

This was just the tip of the iceberg. We barely scratched the surface of everything that’s possible in the terminal.

fzf is a powerful and very flexible fuzzy finder for the command-line. zoxide, bat and fd are just a few examples of modern replacements for standard command-line tools like ls, cat and find.

There are terminal multiplexers out there, that are independent of the terminal emulator you use.tmux is probably the most popular, but there’s also zellij written in Rust that is gaining more followers. You could also take a look at good old GNU Screen.

Cool TUIs) like lazygit or the awesome terminal JSON viewer fx can really make your work easier and more enjoyable.

And then there’s Neovim, a Vim-fork, that is very extensible with plugins written in Lua and can be configured to exactly match your needs.

I will be posting more content about some of those things. I hope you enjoyed this little journey and we’ll meet again, soon.