DEV Community

Recca Tsai
Recca Tsai

Posted on • Originally published at recca0120.github.io

zoxide: Give cd a Memory — Jump to Any Directory in Two Keystrokes

Originally published at recca0120.github.io

My projects are scattered across ~/Sites, ~/WebstormProjects, ~/GolandProjects, ~/PycharmProjects — long paths, no single root. For years I either mashed cd ~/Sites/recca012<TAB> or dragged folders from Finder into the terminal. Then I installed zoxide. Now cd recca gets me there.

The trick is that my cd is no longer a shell builtin. It's zoxide's replacement — same behavior as the original, plus memory.

What frecency Means

zoxide's algorithm is called frecency (frequency + recency). Every directory you visit earns a score that climbs with use and decays over time. Type cd foo and zoxide searches its database for paths containing foo, then jumps to the highest-scoring one.

Here's a slice of my own database:

$ zoxide query --score | head -5
 230.0 /Users/recca0120/WebstormProjects
 215.3 /Users/recca0120/Sites
 198.7 /Users/recca0120/Sites/recca0120.github.io
 142.1 /Users/recca0120/Desktop/vscode-phpunit
  98.5 /Users/recca0120/Downloads
Enter fullscreen mode Exit fullscreen mode

Frequently visited projects float to the top; stale ones sink. Data lives in a local file — fully offline, no network calls.

Install and Init

macOS via Homebrew:

brew install zoxide
Enter fullscreen mode Exit fullscreen mode

Linux one-liner:

curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

Then initialize in your shell config. The key decision is whether to use --cmd cd:

Mode Command Effect
Default zoxide init <shell> Adds z, zi commands. Builtin cd untouched
Replace cd zoxide init --cmd cd <shell> Replaces cd outright

I went with the latter. I use fish shell, so my config reads:

# ~/.config/fish/config.fish
zoxide init --cmd cd fish | source
Enter fullscreen mode Exit fullscreen mode

For zsh / bash, use eval:

# ~/.zshrc
eval "$(zoxide init --cmd cd zsh)"
Enter fullscreen mode Exit fullscreen mode

Why replace cd wholesale? Because zoxide's cd is a superset of the builtin: absolute paths, relative paths, cd -, cd .. all still work. Frecency lookup only kicks in when the argument isn't a valid path. No regression risk.

Three Daily Workflows

1. Keyword jumps. Skip full paths — just type a fragment of the directory name:

cd github          # → ~/Sites/recca0120.github.io
cd webstorm        # → ~/WebstormProjects
cd phpunit         # → ~/Desktop/vscode-phpunit
Enter fullscreen mode Exit fullscreen mode

2. Multi-keyword filtering. When names collide, chain keywords to narrow down:

cd sites blog      # → ~/Sites/scu_blog
cd projects cc     # → ~/WebstormProjects/cc-office
Enter fullscreen mode Exit fullscreen mode

Match rule: every keyword must appear in the path, and the last one must be in the final segment.

3. Interactive selection via zi. When you can't recall the keyword or have multiple candidates:

cdi               # since I used --cmd cd, zi becomes cdi
Enter fullscreen mode Exit fullscreen mode

This opens an fzf UI listing all candidates with live fuzzy filtering. Install fzf first if you haven't: brew install fzf.

Advanced Tricks

Space-triggered completion. In fish, typing cd mydir<SPACE> lists multiple candidates — handy when directories share names. Fish users can also install an enhanced completion pack:

fisher install icezyclon/zoxide.fish
Enter fullscreen mode Exit fullscreen mode

Query without jumping. Preview where zoxide would take you, without actually going:

zoxide query github
# → /Users/recca0120/Sites/recca0120.github.io

zoxide query --list github    # list all matches
zoxide query --score          # view frecency scores
Enter fullscreen mode Exit fullscreen mode

Manually register a directory. For a freshly cloned project you haven't visited yet:

zoxide add ~/projects/new-repo
Enter fullscreen mode Exit fullscreen mode

Exclude noise. /tmp, node_modules, and friends clutter the database:

set -gx _ZO_EXCLUDE_DIRS "/tmp/*" "*/node_modules/*" "$HOME/.cache/*"
Enter fullscreen mode Exit fullscreen mode

Echo destination before jumping. Helps catch wrong jumps:

set -gx _ZO_ECHO 1
Enter fullscreen mode Exit fullscreen mode

Migrate from older tools. autojump, fasd, z.lua all have import paths:

zoxide import --from=autojump ~/.local/share/autojump/autojump.txt
zoxide import --from=z ~/.z
Enter fullscreen mode Exit fullscreen mode

Combining with yazi and tmux

My .zshrc has a function y that syncs yazi's final directory back to the shell on exit:

function y
    set tmp (mktemp -t "yazi-cwd.XXXXXX")
    yazi $argv --cwd-file="$tmp"
    set cwd (cat -- "$tmp")
    if [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]
        cd -- "$cwd"  # this cd is zoxide
    end
    rm -f -- "$tmp"
end
Enter fullscreen mode Exit fullscreen mode

The trailing cd is zoxide's version, so directories I browse through yazi also feed into the frecency database. The two tools cross-pollinate — both get smarter with use.

In tmux, each pane is its own shell, but zoxide's database is shared globally. Visit a directory in pane A and pane B can jump there with cd foo.

When Not to Use --cmd cd

Honestly, --cmd cd isn't uncontroversial. Arguments against overriding the builtin:

  • Shell scripts might accidentally inherit zoxide behavior
  • Shared terminals could confuse other users
  • Certain cd edge cases (like CDPATH) may behave differently

zoxide's implementation only overrides cd in interactive shells, so the first concern is mostly academic. But if you value purity, sticking with the default z / zi gets you 95% of the benefit — you just have to pause each time to pick cd vs. z.

Personally, I prefer --cmd cd. Muscle memory doesn't want to change, so the tool should adapt to the human, not the other way around.

References

Top comments (0)