My projects are scattered across a handful of directories with long, inconsistent paths. For years I either mashed cd ~/some/long/path<TAB> or dragged folders from Finder into the terminal. Then I installed zoxide. Now two or three characters is all it takes.
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 what the database looks like:
| |
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:
| |
Linux one-liner:
| |
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:
| |
For zsh / bash, use eval:
| |
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:
| |
2. Multi-keyword filtering. When names collide, chain keywords to narrow down:
| |
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:
| |
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:
| |
Query without jumping. Preview where zoxide would take you, without actually going:
| |
Manually register a directory. For a freshly cloned project you haven’t visited yet:
| |
Exclude noise. /tmp, node_modules, and friends clutter the database:
| |
Echo destination before jumping. Helps catch wrong jumps:
| |
Migrate from older tools. autojump, fasd, z.lua all have import paths:
| |
Combining with yazi and tmux
My .zshrc has a function y that syncs yazi’s final directory back to the shell on exit:
| |
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
cdedge cases (likeCDPATH) 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.
