How to Use fzf + ripgrep to Instantly Find Anything
In the vast oceans of code and files that developers and power users navigate daily, the ability to find exactly what you’re looking for – instantly – is not just a convenience, it’s a superpower. Traditional grep and find commands, while foundational, often fall short in terms of speed, user experience, and intelligence when dealing with modern, large-scale projects.
Enter fzf and ripgrep (often shortened to rg). Individually, they are formidable tools. ripgrep is a blazing-fast, intelligent line-oriented search tool, and fzf is a general-purpose command-line fuzzy finder. But when combined, they create a synergistic workflow that can transform how you interact with your filesystem, allowing you to locate files by name or content with unparalleled speed and interactive filtering.
This post will guide you through setting up, understanding, and mastering fzf and ripgrep to unlock a new level of command-line productivity.
Why ripgrep? The Speed Demon of Search
ripgrep is a command-line utility that recursively searches directories for a regex pattern. What sets it apart from traditional grep (and even other modern alternatives like ag - The Silver Searcher) is its incredible speed and sensible defaults.
Key Advantages of ripgrep
- Blazing Fast:
ripgrepis written in Rust, which allows it to leverage highly optimized regex engines and parallel processing. It often outperformsgrep,ag, andgit grepby a significant margin, especially on large repositories. - Smart Defaults: By default,
ripgreprespects your.gitignorefiles and intelligently skips hidden files/directories and binary files. This means it only searches relevant code, drastically reducing search time and noise. - Cross-Platform: It works seamlessly on Linux, macOS, and Windows.
- User-Friendly Output: Its output is clean, colorized, and easy to parse, showing filenames, line numbers, and the matching line.
Installation
Installation is straightforward across most platforms:
- macOS (Homebrew):
brew install ripgrep - Linux (apt, dnf, pacman, etc.):
# Debian/Ubuntu sudo apt install ripgrep # Fedora sudo dnf install ripgrep # Arch Linux sudo pacman -S ripgrep - Other/Manual: You can also download pre-compiled binaries from the ripgrep GitHub releases page and place them in your
PATH.
Basic ripgrep Usage
Once installed, try it out:
- Search for a pattern in the current directory:
rg "my_function" - Case-insensitive search:
rg -i "error_handler" - Search only for files with a specific extension:
rg "user_data" -g "*.js" - List only filenames that contain a match:
rg --files-with-matches "TODO"
You’ll quickly notice its speed and the clean, readable output.
Why fzf? The Interactive Fuzzy Finder
fzf is a general-purpose command-line fuzzy finder. It reads list input from stdin, launches an interactive fuzzy selection UI, and writes the selected items to stdout. Its true power lies in its interactivity and flexibility, allowing it to integrate with virtually any command that outputs a list.
Key Advantages of fzf
- Fuzzy Matching: You don’t need to type the exact characters or even in order.
fzfwill “fuzzy” match your input against the list, making it incredibly forgiving and fast for recall. - Interactive Interface: As you type,
fzffilters the list in real-time. This provides instant feedback and allows for quick refinement of your search. - Highly Customizable:
fzfcan be configured with various options, including preview windows, key bindings, and default commands. - Seamless Integration: It’s designed to be piped with other commands (
ls | fzf,git branch | fzf,history | fzf), acting as an interactive filter for virtually any list of items.
Like ripgrep, fzf is easy to install:
- macOS (Homebrew):
(Follow the on-screen prompts for setting up keybindings for your shell, e.g.,
brew install fzf # To install fuzzy completion and key bindings, run: $(brew --prefix)/opt/fzf/installCTRL-Rfor history search). - Linux (apt, dnf, pacman, etc.):
If using package managers, you might need to manually run the install script for keybindings, or check your distribution’s documentation.
# Debian/Ubuntu sudo apt install fzf # Fedora sudo dnf install fzf # Arch Linux sudo pacman -S fzf - Git Clone (recommended for latest features):
This method gives you the latest version directly from the source and prompts you to set up shell integrations.
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf ~/.fzf/install
Basic fzf Usage
Once fzf is installed and integrated into your shell, try these:
- Filter directory contents:
Type a few letters, and see how the list narrows down. Press
ls -l | fzfEnterto select an item. - Search your shell history (
CTRL-Rafter installation): PressCTRL-Rin your terminal. This opensfzfwith your command history, allowing you to fuzzy search past commands. - Switch Git branches:
(You could extend this to
git branch | fzfgit checkout $(git branch | fzf)for interactive branch switching.)
The Synergistic Power: fzf + ripgrep
This is where the magic happens. We combine ripgrep’s lightning-fast search capabilities with fzf’s interactive filtering and selection. The goal is to be able to type a search query, see live results from ripgrep, preview the matching files/lines, and then instantly jump to the selected file in your editor at the correct line number.
Core Concept: Pipelining ripgrep to fzf
The simplest form of integration is to pipe ripgrep’s output directly into fzf.
rg "my_function" | fzfThis will run ripgrep for “my_function”, then pipe all matching lines into fzf. You can then fuzzy filter these lines. This is useful for quickly sifting through a large number of matches. However, it only shows the line, not the surrounding context, and doesn’t directly open the file.
The Ultimate Workflow: Interactive ripgrep with Live Preview and Editor Integration
This is the killer combination. We want fzf to act as the interactive prompt for ripgrep, allowing ripgrep to re-search as you type, show matching lines with context, and then open the selected file in your preferred editor at the correct line number.
For this, we’ll leverage fzf’s --bind 'change:reload(...)' feature and a preview window. We’ll also typically use bat for enhanced code previews, so make sure to install it:
- macOS (Homebrew):
brew install bat - Linux (apt, dnf, pacman, etc.):
sudo apt install bat(or from bat GitHub releases)
Now, let’s define a function in your ~/.bashrc, ~/.zshrc, or equivalent shell configuration file. We’ll call it rgf (ripgrep-fzf).
# Define a function 'rgf' for ripgrep-fzf search
rgf() {
# Check if bat is installed for enhanced previews, otherwise fall back to cat
local PREVIEW_CMD="cat"
if command -v bat &> /dev/null; then
PREVIEW_CMD="bat --color=always"
else
echo "Warning: 'bat' not found. Install it for better previews (e.g., 'brew install bat')." >&2
fi
# Ensure fzf-tmux is preferred for a floating window if tmux is running
local FZF_BIN="fzf"
if command -v fzf-tmux &> /dev/null && [ -n "$TMUX" ]; then
FZF_BIN="fzf-tmux -p" # -p for popup window
fi
# The core fzf + ripgrep command
# - --bind 'change:reload(...)': As you type (query changes), ripgrep is re-run
# - `rg --column --line-number --no-heading --color=always --smart-case --fixed-strings {q} || true`
# - `--column`: Include column number in output (useful but not used in this specific nvim binding for simplicity).
# - `--line-number`: Include line number. Essential for opening files at specific lines.
# - `--no-heading`: Don't print filename headers (fzf handles this better in preview).
# - `--color=always`: Force color output, which fzf's `--ansi` option then interprets.
# - `--smart-case`: Search case-insensitively unless the pattern contains uppercase letters.
# - `--fixed-strings`: Treat pattern as a literal string, not a regex. Remove for regex search.
# - `{q}`: This is fzf's placeholder for the current query string you are typing.
# - `|| true`: Prevents fzf from erroring out if ripgrep finds no matches (ripgrep exits with 1).
# - --ansi: Tells fzf to interpret ANSI color codes from ripgrep.
# - --preview "$PREVIEW_CMD {1} --highlight-line {2}": Shows content of the selected file.
# - `{1}`: The first field extracted by `--delimiter :` (which is the filename).
# - `{2}`: The second field (line number).
# - `--highlight-line {2}`: `bat` specific, highlights the matching line.
# - --delimiter : --nth 3..: Parses ripgrep's output.
# - ripgrep output format: `filename:line_number:column_number:content` (with `--column`)
# - `--delimiter :`: Splits each line by colon.
# - `--nth 3..`: Displays only the content starting from the 3rd field. This hides filename and line number in the main fzf window, keeping it clean.
# - --bind 'enter:execute(nvim +{2} {1})': When you press Enter, execute this command.
# - `nvim +{2} {1}`: Opens the file (`{1}`) in Neovim at the specified line number (`{2}`).
# Adjust `nvim` to your preferred editor (e.g., `vim`, `code --goto`).
# - --header: Provides helpful text at the top of the fzf window.
$FZF_BIN \
--bind "change:reload(rg --column --line-number --no-heading --color=always --smart-case --fixed-strings {q} || true)" \
--ansi \
--preview "$PREVIEW_CMD {1} --highlight-line {2}" \
--delimiter : \
--nth 3.. \
--bind 'enter:execute(nvim +{2} {1})' \
--header "Type to search with ripgrep. Enter to open in nvim. (C-d to cancel)"
}
# Add a simple alias to quickly get to your dotfiles directory and use rgf
alias dotfrg='cd ~/.dotfiles && rgf' # Adjust ~/.dotfiles to your actual dotfiles pathAfter adding this function to your shell configuration file (e.g., ~/.zshrc), remember to source it: source ~/.zshrc (or open a new terminal).
Now, simply type rgf and press Enter. An fzf window will pop up. Start typing your search query. As you type, ripgrep will run in the background, and fzf will display the matching lines in the main window and a preview of the selected file’s content (with the line highlighted) in the preview pane. When you find what you need, hit Enter, and it will open directly in nvim at the correct line!
Note: If you don’t use tmux, remove the fzf-tmux -p part and just use fzf. The experience will be similar, but fzf will run within your current terminal pane rather than a floating window.
Other Powerful Combinations
Beyond the live-search function, there are other useful ways to combine fzf and ripgrep:
1. Finding Files by Name (respecting .gitignore)
While find or fd (find-alternative) are common for file name searches, ripgrep can also list files. Its advantage is its built-in .gitignore awareness.
# List all tracked files (by ripgrep's definition, respecting .gitignore) and fuzzy find
rg --files | fzf --preview 'bat --color=always {}' --bind 'enter:execute(nvim {})'
# Or, if you only want files containing a specific pattern (e.g., "TODO"), then select which file to open
rg --files-with-matches "TODO" | fzf -m --preview 'bat --color=always {}' | xargs -r nvim
# -m: allows multi-selection in fzf (use Tab to select multiple, Enter to confirm)
# xargs -r nvim: opens selected files in nvim. `-r` prevents running nvim if no files selected.This is incredibly useful for navigating large projects when you know a file contains something but might not remember its exact name.
2. Interactive Grep on a Subset of Files
You can combine ripgrep to narrow down the files, then fzf to pick which one to search, and then another ripgrep inside of that specific file.
# Find files based on a pattern, then open the chosen one and search within it.
select_and_grep() {
local selected_file=$(rg --files-with-matches "$1" | fzf)
if [[ -n "$selected_file" ]]; then
# Now, you could either open the file or even run another rg specifically on it.
# Example: Open the file and then let the user search within it manually (or use editor's search)
nvim "$selected_file"
# Or, to grep again *within* that file, though less common with fzf's preview already showing content
# rg "$1" "$selected_file" | fzf
fi
}
alias sgr='select_and_grep' # Usage: sgr "my_pattern"Advanced Customization and Tips
-
FZF_DEFAULT_OPTS: You can set default options forfzfusing this environment variable in your~/.bashrcor~/.zshrc. For example:export FZF_DEFAULT_OPTS=" --reverse --height 40% --layout=reverse --ansi --info=inline --border "This makes
fzfappear at the bottom, occupy 40% of the screen, and have a border. -
FZF_DEFAULT_COMMAND: If you always wantfzfto search a specific set of files by default (e.g., usingrg --filesinstead offind), you can set this variable:export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git/*"'This would make
fzf(when called without piping from another command) list files usingripgrep, including hidden files but excluding the.gitdirectory. -
Shell Keybindings: Ensure you’ve run the
fzf/installscript or manually set up keybindings.CTRL-T(for inserting selected files/directories) andCTRL-R(for history search) are incredibly useful. -
Contextual Preview: The
batutility is key for good code previews infzf. It handles syntax highlighting, line numbers, and automatically respects terminal width. For other file types, considerexiftoolfor images orpandocfor documents in the preview script. -
Multi-Select: Remember the
-moption forfzfto select multiple items (usingTab) and thenEnterto output them. This is very powerful when combined withxargs. Example:rg --files-with-matches "TODO" | fzf -m | xargs -r vim
Troubleshooting and Considerations
- Performance on Extremely Large Repos: While
ripgrepis fast, searching across truly massive, unindexed repositories (e.g., kernel source trees) can still take a moment. Thefzfinteractive reload mechanism is best for “typing to filter” scenarios within a reasonably sized project. - Editor Command: Make sure to replace
nvimwith your actual editor command (e.g.,vim,code --goto,subl). - Shell Compatibility: The examples primarily use Bash/Zsh syntax. If you’re on a different shell (e.g., Fish), you’ll need to adapt the function definitions accordingly. Fish shell has its own
fzfintegrations that are often quite seamless. - Regex vs. Fixed Strings:
ripgrepuses regular expressions by default. For literal string searches, use the-For--fixed-stringsflag, as shown in thergffunction. This can prevent unexpected behavior with special characters.
Conclusion
The combination of fzf and ripgrep is a game-changer for anyone spending significant time in the terminal. ripgrep provides the unparalleled speed and intelligence needed for searching vast codebases, while fzf layers on an intuitive, interactive, and highly customizable fuzzy-finding interface.
By investing a small amount of time in setting up the rgf function and understanding its components, you’ll unlock a highly efficient workflow for finding files and content. No more endless grep outputs or tedious find commands. With fzf and ripgrep, you’ll instantly find anything, empowering you to navigate your digital world with precision and speed. Embrace this dynamic duo and elevate your command-line prowess!