fzf & zoxide — Fuzzy Finder & Smart Directory Jump
In this tutorial, you'll learn about fzf & zoxide. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
fzf is a general-purpose fuzzy finder that lets you search and select from any list — files, processes, commands, Git branches — with instant filtering. zoxide learns your directory navigation patterns and replaces cd with a jump command that learns your habits.
What You'll Learn
How to use fzf for interactive file searching, command history, Process killing, and Git operations. How zoxide learns your directory patterns and lets you jump across the filesystem with a few keystrokes. How to combine both tools for a seamless terminal workflow.
Why fzf and zoxide Matter
Navaging the terminal is a core developer activity — finding files, switching directories, recalling commands. Without fzf and zoxide, you type full paths, scroll through history, or use find and grep. With fzf, you press Ctrl+T and instantly filter files by partial name. With zoxide, z proj takes you to /home/user/work/project/src because it knows that is where you go most. DodaZIP's CI team uses fzf for interactive log inspection — journalctl -u build | fzf — filtering thousands of log lines to find relevant entries in seconds.
Learning Path
flowchart LR A[bat & delta] --> B[fzf & zoxide
You are here] B --> C[Starship Prompt] B --> D[Ripgrep & fd] style B fill:#f90,color:#fff
fzf — Fuzzy Finder
Installation
# macOS
brew install fzf
# Ubuntu/Debian
sudo apt install fzf
# Fedora
sudo dnf install fzf
# Arch
sudo pacman -S fzf
# Install key bindings and completion
$(brew --prefix)/opt/fzf/install
# Or for Linux:
/usr/share/doc/fzf/examples/install --key-bindings --completion --no-update-rc
Key Bindings
After installing key bindings, three shortcuts transform your shell:
| Binding | Action |
|---|---|
Ctrl+T |
Paste selected file/directory path into command |
Ctrl+R |
Fuzzy-search command history |
Alt+C |
cd into selected directory |
# Examples using key bindings:
vim **<TAB> # Tab completion triggers fzf for files
kill -9 **<TAB> # fzf lists running processes
ssh **<TAB> # fzf lists SSH hosts from known_hosts
cd **<TAB> # fzf lists directories (if zoxide integration)
Basic Usage
# Pipe any list to fzf
find . | fzf
ps aux | fzf
git branch | fzf
history | fzf
# Select multiple items
find . | fzf -m
# Execute command on selection
fzf --bind 'enter:execute(nvim {})'
# Preview file content
fzf --preview 'cat {}'
# Preview with syntax highlighting
fzf --preview 'bat --color=always {}'
# Preview directory contents
fzf --preview 'ls -la {}' --preview-window=right:60%
Advanced fzf
# Search with fzf and open in editor
nvim $(fzf --preview 'bat --color=always {}')
# Kill a process interactively
kill -9 $(ps aux | fzf | awk '{print $2}')
# Git checkout branch interactively
git checkout $(git branch --all | fzf | tr -d ' *')
# Search code with rg and preview with bat
rg -l "TODO" | fzf --preview 'bat --color=always {}'
# SSH to a host
ssh $(grep -E "^Host " ~/.ssh/config | awk '{print $2}' | fzf)
# Docker operations
docker stop $(docker ps | fzf | awk '{print $1}')
docker logs $(docker ps | fzf | awk '{print $1}') -f
Custom FZF Configuration
# ~/.zshrc or ~/.bashrc
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow'
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'
export FZF_CTRL_T_COMMAND='fd --type f --hidden'
export FZF_CTRL_T_OPTS='--preview "bat --color=always {}"'
export FZF_ALT_C_COMMAND='fd --type d --hidden'
export FZF_ALT_C_OPTS='--preview "ls -la {}"'
# Use ripgrep for CTRL-R (faster than reading .bash_history)
export FZF_CTRL_R_OPTS='--sort --preview "echo {}" --preview-window down:3:wrap'
fzf with tmux
# Open fzf in a tmux popup
fzf --tmux
# With custom size
fzf --tmux 80%
fzf --tmux 80%,40% # 80% width, 40% height
Git Integration Script
# ~/bin/git-fzf.sh — interactive Git operations
#!/bin/bash
case "$1" in
checkout)
git branch --all | fzf | tr -d ' *' | xargs git checkout
;;
log)
git log --oneline --graph --all | fzf --preview 'echo {} | cut -d" " -f1 | xargs git show' | cut -d" " -f1 | xargs git show
;;
diff)
git log --oneline | fzf -m | cut -d" " -f1 | xargs git diff
;;
stash)
git stash list | fzf | cut -d: -f1 | xargs git stash apply
;;
esac
zoxide — Smarter Directory Jump
Installation
# macOS
brew install zoxide
# Ubuntu/Debian
sudo apt install zoxide
# Fedora
sudo dnf install zoxide
# Arch
sudo pacman -S zoxide
# With curl
curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
Shell Integration
# Add to ~/.zshrc or ~/.bashrc:
eval "$(zoxide init zsh)" # for zsh
eval "$(zoxide init bash)" # for bash
eval "$(zoxide init fish)" # for fish
Basic Usage
# Replace cd with z
z projects
z work/src
# Jump to the most recent match
z projects
# Interactive selection with fzf
zi
# List all directories with their rankings
zoxide query -l
# Jump to a subdirectory
z projects/src/utils
# Add a directory to the database manually
zoxide add /path/to/directory
zoxide in Action
# Instead of:
cd ~/work/project/src/components/
# Just type:
z comp
# zoxide finds the best match based on frequency and recency
# Multiple matches — zoxide shows the top choice:
# If multiple directories match "comp":
# 1. ~/work/project/src/components (visited 45 times)
# 2. ~/work/other-project/comp (visited 3 times)
# z picks the first one
# Use `zi` for interactive selection when unsure
zi comp
# Opens fzf with matching directories ranked by score
Advanced zoxide
# Exact match (no fuzzy)
z '^/home/user/projects$'
# Best match for subdirectory
z 'src' # matches ~/work/project/src
# Remove a directory from database
zoxide remove /old/path
# Purge directories that no longer exist
zoxide query -l | while read d; do [ -d "$d" ] || zoxide remove "$d"; done
# Use with cd for fallback
unalias cd 2>/dev/null
cd() {
if [ "$#" -eq 0 ]; then
builtin cd ~
elif [ "$#" -eq 1 ] && [ -d "$1" ]; then
builtin cd "$1"
elif [ "$#" -eq 1 ]; then
z "$1"
else
builtin cd "$@"
fi
}
Integration with fzf
# zoxide + fzf: interactive directory jumping
# Already available as `zi` after zoxide init
# Or manually:
zoxide query -l | fzf --preview 'ls -la {}'
# Combined with fd for file search:
cd $(zoxide query -l | fzf)
vim $(fd -t f | fzf)
Real-World Workflow
# Morning routine with fzf + zoxide:
# 1. Jump to project
z myproject
# 2. Find the file you need
nvim $(fzf --preview 'bat --color=always {}')
# 3. Search for the function you need
rg "def process_" -l | fzf
# 4. Git operations with fzf
git log --oneline | fzf | cut -d" " -f1 | xargs git show
# 5. Jump to logs directory
z logs
tail -f $(fzf)
# 6. Find that command from yesterday
Ctrl+R -> type "deploy" -> fuzzy filter -> Enter
Common Errors
1. fzf Shows "command not found" After Installation
The fzf binary may not be in PATH. On Ubuntu with apt, fzf is in /usr/bin/fzf. Verify: which fzf. Re-run the install script for key bindings.
2. zoxide Scores Not Accurate
zoxide ranks by frequency and recency. If your most-visited directory is not ranking first, the algorithm may need more data. Use zoxide query -l to see scores.
3. Ctrl+T Does Nothing
The key bindings script was not sourced. Add eval "$(fzf --zsh)" or eval "$(fzf --bash)" to your shell config and reload.
4. zoxide "zi" Not Working
The interactive mode requires fzf to be installed. Ensure both fzf and zoxide are installed, and eval "$(zoxide init zsh)" is in your config.
5. fzf Preview Shows Nothing
The preview command may not be found. Use full paths or ensure the tool (bat, cat, ls) is installed. Check with fzf --preview 'cat {}'.
6. zoxide Database Out of Sync
If you move or rename directories, zoxide still has the old paths. Remove them: zoxide remove /old/path.
7. fzf Performance Is Slow on Large Repos
If FZF_DEFAULT_COMMAND= uses find on a repo with node_modules, fzf will be slow. Use fd instead of find: export FZF_DEFAULT_COMMAND='fd --type f'.
Practice Questions
1. What are the three default fzf key bindings?
Ctrl+T (files), Ctrl+R (history), Alt+C (directories).
2. How does zoxide decide which directory to jump to? It uses a frecency algorithm — a combination of frequency (how often you visit) and recency (when you last visited).
3. How do you make fzf preview file contents while searching?
Use --preview 'bat --color=always {}' or --preview 'cat {}'.
4. What is the difference between z and zi in zoxide?
z jumps directly to the best match. zi opens an interactive fzf selection of all matches.
5. How do you use fzf to kill a Process interactively?
kill -9 $(ps aux | fzf | awk '{print $2}') — fzf selects the Process, awk extracts the PID.
Challenge: Create a shell function called nav that: (1) uses zoxide to list the top 20 directories, (2) displays them in fzf with directory contents in the preview pane, (3) on selection, changes to that directory, (4) if no directories match, uses fd to search the filesystem. Also create a gf (git find) function that uses fzf to interactively search and checkout branches, view commits, and apply stashes.
What's Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-24.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro