← Back to all posts

zoxide: The cd Command That Learns Where You Work

Title card showing a jump arcing past intervening directories to a highlighted one, above a frecency ranking

TL;DR: zoxide replaces cd. Instead of typing a full path, you type a fragment of a directory name - z warden - and it jumps to the highest-ranked match from your history. The ranking is frecency (frequency + recency), it’s written in Rust, it works in every shell, and it falls back to an fzf picker when several directories match. Set --cmd cd and plain cd becomes the smarter version transparently. The one honest caveat: a fresh install has an empty database, so it’s mediocre for about a week until it learns your habits.


This series is mostly about tools that take something you do dozens of times a day and shave the friction off it. wt did it for branches; sesh did it for sessions. zoxide does it for the single most-run command in any shell: cd.

The problem with cd

cd makes you supply the path. With a deep directory tree and a dozen active projects, that means either remembering exactly where things live or tab-completing your way down level by level:

cd ~/dev/worktrees/drift-warden/feat/39-oci-digest-pinning

You run some version of that hundreds of times a day, and every one of them asks you to hold the directory structure in your head. It’s small, but it’s constant - and constant small friction is exactly what these tools exist to remove. The goal, same as with my note-taking system, is that the tool disappears while you think.

Frecency, explained

zoxide keeps a database of the directories you visit and scores each one by combining two things:

  • Frequency - how often you’ve been there
  • Recency - how recently, with recent visits weighted more heavily

Hence frecency. Scores decay over time (aging), so directories you’ve stopped using fade out of the way instead of cluttering your jumps forever. The upshot: the place you’re most likely to want is almost always the top match for a fragment of its name, and you stop thinking about paths at all - you think about which project and let the tool resolve the path.

It’s not a new idea; it’s the well-executed version of an old one:

ToolLanguageNote
autojumpPythonthe pioneer; slower to start
zshella lightweight script; no interactivity
fasdshellextended z to files as well as directories
zoxideRustthe current standard - cross-shell, fzf, big integration ecosystem

The patterns I actually use

z warden        # jump to the top-ranked directory matching "warden"
z warden oci    # multi-word: both terms must appear in the path
zi warden       # interactive - pick from matches with fzf
z -             # back to the previous directory (like cd -)

z warden covers the vast majority of jumps. z warden oci is what I reach for when a single fragment is ambiguous - both words have to match the path, which usually pins it exactly. And zi opens an fzf picker for the times I genuinely want to eyeball the options.

The setting that makes it feel native is --cmd cd, which replaces the built-in cd with zoxide. No new muscle memory - you keep typing cd, it’s just smart now.

Pairing with wt

This is where it compounds with the rest of the setup. wt solves navigation between git worktrees at the git level; zoxide solves shell navigation in general. They’re complementary, not competing - and they feed each other. When wt checkout drops you into a worktree, zoxide records that path, so the next time you just z <branch-fragment> straight there without going through wt at all. Git-level navigation and shell-level navigation, reinforcing each other.

The caveats the README won’t lead with

Because every tool has an unglamorous side, and knowing it up front saves the disappointment:

  • Cold start. A fresh install has an empty database. For the first few days the jumps can land in the wrong place simply because zoxide hasn’t learned your habits yet - it takes about a week of daily use to become genuinely useful. If you’re coming from autojump, z or fasd, import your existing data (zoxide import) and skip the warm-up entirely.
  • Name collisions. When several projects have identically-named subdirectories (src, tests, docs), a one-word query can jump to the wrong one. The fix is multi-word queries or the zi picker.
  • Stale database. Directories you’ve deleted from disk linger in the database until their score ages out. You can hurry that along with _ZO_MAXAGE.

None of these are dealbreakers, but they’re the difference between “this is broken” and “oh, it just needs a week.”

In my setup

macOS install and shell hook:

brew install zoxide
# add to ~/.zshrc
eval "$(zoxide init zsh --cmd cd)"

On NixOS via Home Manager it’s declarative, and the hook goes in with the same guarded pattern I use for wt - so a machine that doesn’t have zoxide installed still starts a clean shell:

programs.zoxide = {
  enable = true;
  enableZshIntegration = true;
  options = [ "--cmd cd" ];
};

A couple of environment knobs worth knowing: _ZO_EXCLUDE_DIRS keeps directories out of the database (globs, :-separated), and _ZO_DATA_DIR moves the database if you don’t want it in the default location.

That’s the whole tool. z to jump, zi when it’s ambiguous, --cmd cd so you never think about it again - and a week of patience while it learns where you actually work.