I wrote a review of Atuin a few days ago and the tl;dr was that very few dev tools earn a permanent slot in my dotfiles. Most last a week. The bar is high.
Zoxide cleared the same bar with about a tenth of the effort. I installed it two years ago, forgot it existed because it just works, and only remembered it the other day when I sat down at a coworker’s laptop, typed z somerepo, and got “command not found.” That moment of confusion is the strongest possible review I can give a dev tool: I had stopped noticing it because it had stopped being a tool and started being part of how the shell worked.
What Zoxide Is
Zoxide replaces cd. That’s it. You type z <partial-name> and it jumps to the directory you most likely meant.
Underneath that one-line description:
- It tracks every directory you
cdinto. - It scores them by frecency — a blend of frequency and recency. The directories you go to often and recently rank highest.
- When you type
z foo, it picks the highest-ranked directory whose path matchesfooand jumps there. - It is written in Rust, the binary is small, the cold-start cost is unmeasurable.
If you are old enough to remember autojump or z.sh, zoxide is the spiritual descendant: same idea, fast Rust implementation, better defaults, actively maintained.
Installation
On macOS:
brew install zoxide
On Linux, your package manager almost certainly has it, or grab the binary from the zoxide GitHub releases. Then one line in your shell rc:
# zsh
eval "$(zoxide init zsh)"
# bash
eval "$(zoxide init bash)"
# fish
zoxide init fish | source
That’s it. Open a new shell. Start using cd like normal. Zoxide is silently watching and building its database. After a day or two of normal use, z will start working well.
The Two Commands You Use
z foo— jump to the highest-ranked directory matchingfoo.zi foo— interactive picker. Pops up an fzf-style list of all matches, you pick one. Use this whenzjumps somewhere wrong.
That is the entire interface. There is more (z - to go back, zoxide query for raw scoring, zoxide remove to prune entries), but 95% of my usage is z and very occasionally zi.
The Config Change That Matters
Zoxide’s default behavior is “augment cd, don’t replace it.” cd still does what cd always did. z is the new command. That is the safe default.
The change that made zoxide click for me was this line:
eval "$(zoxide init zsh --cmd cd)"
That --cmd cd flag replaces cd entirely. Now cd foo does the zoxide thing. cd ./relative/path still does the old cd thing because zoxide is smart enough to recognize an actual path argument and fall through. And on the rare occasion you really want plain cd, it’s still there as builtin cd.
This is the version I recommend. The mental overhead of remembering “is this a cd situation or a z situation” is gone. Just type cd <whatever>. If it’s a path, it cd’s. If it’s a frecency token, it jumps. Both work. You stop having to think about it.
The two-command default is fine if you want to A/B it for a week before committing. Past that, just rebind cd.
What Sold Me
Three things, in order of how often they save me time.
1. The “I know I have a project called X somewhere” problem disappears
I have on the order of 200 git repos cloned across ~/code, ~/work, ~/forks, and an embarrassing ~/tmp-clones. Before zoxide, getting into one of them was a find ~ -type d -name foo or a cd ~/code/sub/long/path. With zoxide:
cd jpk-io
I am in ~/repos/jpk.io. I do not need to remember where it lives. I do not need tab completion. I do not need to type the full path. The shell knows where I go and takes me there.
2. It composes with everything
Zoxide does not own your shell. It does not own your prompt. It does not fight with your existing aliases, your direnv, your shell history. It is one binary and one shell hook. If you uninstall it, your cd is back to normal, your history is intact, and nothing breaks.
This is the same property that made me trust Atuin. The tools that earn permanent slots in my dotfiles are the ones that can be removed without consequence.
It also composes well with Atuin and with fzf. zi uses fzf if it’s installed. Atuin’s directory-scoped search becomes more useful when you actually get to the right directory faster. The three together are a meaningful jump in shell quality of life and the combined install time is about 90 seconds.
3. The frecency model is right
The first month I used zoxide, I wondered whether pure-recency or pure-frequency would have been better. The answer, in retrospect, is neither.
Recency-only means a one-off cd ~/Downloads last night outranks the project I work in every day. Frequency-only means a project I haven’t touched in six months outranks the one I started yesterday. Frecency — both, weighted — does the obvious right thing for the obvious common cases. I have never had to think about it.
What Annoys Me (A Short List)
- The database is in
~/.local/share/zoxide/db.zoby default. If you have a dotfiles repo that you want to sync across machines, you do not want this synced. It is per-machine state, not config. Add it to your gitignore. - The first week is meh. Zoxide needs a few days of
cd-ing for the frecency database to get useful. Until then,z foojumps to weird places because every match is tied at “ranked once.” This is a one-time problem; just push through it. - It will not jump to a directory it has never seen. Obvious in hindsight, surprising the first time. If you cloned a new repo and haven’t
cd’d into it yet,z newrepowill not work. You have tocd /path/to/newrepoonce. (With--cmd cd, that’s the samecdyou’d already type, so this is a non-issue once you rebind.) - Path-style arguments win over name-style.
cd srcin a repo with asrcdirectory will go to./src, not jump elsewhere. This is the correct behavior, but if you wanted to jump elsewhere you needcd ~srcor just type more of the name.
None of these are dealbreakers. The first is a one-line fix. The rest are documented.
My Config
Almost nothing. The entire zoxide config in my zshrc:
# Replace cd with zoxide; falls through to builtin for real paths
eval "$(zoxide init zsh --cmd cd)"
I do not set environment variables. I do not tune the scoring algorithm. The defaults are fine. If you want to make zoxide more aggressive about pruning stale entries:
export _ZO_MAXAGE=10000
That caps the database at 10,000 entries (default is 1,000), which on a busy machine is more useful. Past that I do not touch it.
Versus the Alternatives
autojump: same idea, Python, slower, less actively maintained. Zoxide is the strict upgrade.z.sh(rupa/z): the original, shell script. Works. Slower than zoxide on a large database. Zoxide is whatz.shwould be if rewritten today.fasd: tried to do directories and files. Did neither as well as zoxide does directories. Quietly abandoned several years ago.- fzf alone:
cd $(find ~ -type d | fzf)is fine but does not learn your habits. Zoxide is “fzf with memory.”
If you are using any of the older tools, switching to zoxide is a five-minute job and you will not look back.
Should You Install It?
Yes. Of all the dev tools I have ever recommended, zoxide is the one with the smallest install cost and the highest return rate. Five minutes. No config required. No commitment. Works on any shell on any OS. The downside is essentially zero and the upside is that cd stops being a thing you think about.
If you also live in a terminal heavily, pair it with Atuin for shell history and a justfile for project commands. That trio — zoxide, Atuin, just — covers about 80% of the friction I had with raw shell flow five years ago.
The Bottom Line
Zoxide is the single highest-ROI shell tool I have ever installed. The install is a one-liner, the config that matters is a one-line flag, and after two days of use you will stop typing full paths forever. I forgot it existed because it became invisible. That is the strongest endorsement I know how to give.
Install it tonight. You will be using it tomorrow and you will not remember a time before it.