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:
ripgrep
is written in Rust, which allows it to leverage highly optimized regex engines and parallel processing. It often outperformsgrep
,ag
, andgit grep
by a significant margin, especially on large repositories. - Smart Defaults: By default,
ripgrep
respects your.gitignore
files 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.
fzf
will “fuzzy” match your input against the list, making it incredibly forgiving and fast for recall. - Interactive Interface: As you type,
fzf
filters the list in real-time. This provides instant feedback and allows for quick refinement of your search. - Highly Customizable:
fzf
can 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):
brew install fzf # To install fuzzy completion and key bindings, run: $(brew --prefix)/opt/fzf/install
CTRL-R
for history search). - Linux (apt, dnf, pacman, etc.):
# Debian/Ubuntu sudo apt install fzf # Fedora sudo dnf install fzf # Arch Linux sudo pacman -S fzf
- Git Clone (recommended for latest features):
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:
ls -l | fzf
Enter
to select an item. - Search your shell history (
CTRL-R
after installation): PressCTRL-R
in your terminal. This opensfzf
with your command history, allowing you to fuzzy search past commands. - Switch Git branches:
git branch | fzf
git 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" | fzf
This 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 path
After 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 forfzf
using this environment variable in your~/.bashrc
or~/.zshrc
. For example:export FZF_DEFAULT_OPTS=" --reverse --height 40% --layout=reverse --ansi --info=inline --border "
This makes
fzf
appear at the bottom, occupy 40% of the screen, and have a border. -
FZF_DEFAULT_COMMAND
: If you always wantfzf
to search a specific set of files by default (e.g., usingrg --files
instead 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.git
directory. -
Shell Keybindings: Ensure you’ve run the
fzf/install
script or manually set up keybindings.CTRL-T
(for inserting selected files/directories) andCTRL-R
(for history search) are incredibly useful. -
Contextual Preview: The
bat
utility is key for good code previews infzf
. It handles syntax highlighting, line numbers, and automatically respects terminal width. For other file types, considerexiftool
for images orpandoc
for documents in the preview script. -
Multi-Select: Remember the
-m
option forfzf
to select multiple items (usingTab
) and thenEnter
to 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
ripgrep
is fast, searching across truly massive, unindexed repositories (e.g., kernel source trees) can still take a moment. Thefzf
interactive reload mechanism is best for “typing to filter” scenarios within a reasonably sized project. - Editor Command: Make sure to replace
nvim
with 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
fzf
integrations that are often quite seamless. - Regex vs. Fixed Strings:
ripgrep
uses regular expressions by default. For literal string searches, use the-F
or--fixed-strings
flag, as shown in thergf
function. 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!