I used to open files the slow way: cd into a directory, ls to see what’s there, maybe find with a half-remembered filename, then finally pass it to whatever program I needed. Every time, I’d lose a few seconds hunting for the right path. Multiply that by dozens of files a day, and it adds up to real friction.
Then I found fzf, and file selection stopped being a chore. fzf is a fuzzy finder that turns file selection into a fast, interactive search.
What fzf actually is
fzf is a command-line fuzzy finder. It takes a list of things (files, directories, command history, git branches, anything) and lets you interactively narrow that list by typing a few characters. You don’t need to remember exact filenames or paths. Type fragments in any order, and fzf finds matches instantly.
Junegunn Choi built it in Go. It ships as a single binary with no dependencies. You can install it anywhere, and it just works.
The word “fuzzy” is the important part. Traditional search tools like grep or find require you to know what you’re looking for. fzf flips that around. You start typing, and it shows you what matches. The characters can sit anywhere in the filename. Type cntlr and it’ll match controller. Type idx and it’ll find index.md.
The mental model: fzf is a filter
The concept that makes fzf click is simple: fzf is a filter. It reads lines from standard input, lets you pick one interactively, and writes your selection to standard output.
This is textbook Unix philosophy. fzf doesn’t know or care what it’s filtering. It doesn’t know what you’ll do with the result. It does one thing well: let you pick from a list, fast.
That’s why it’s so versatile. Any program that accepts a filename as an argument can benefit from fzf. The pattern is always the same:
command $(fzf)fzf pops up, you pick a file, and the shell passes the result to command. That’s the whole trick.
Why fuzzy matching changes how you work
Traditional file finding requires you to think in paths. You have to remember the directory structure, the exact filename, maybe even the extension. Your brain does work that a computer should be doing.
Fuzzy matching lets you think in fragments. You remember that the file had “config” and “prod” somewhere in the name? Type confprod and fzf will find config/production.yaml buried three directories deep.
Under the hood, fzf uses a modified Smith-Waterman algorithm (borrowed from bioinformatics, of all places) to score matches. Every character in your query must appear in the result, in order, though they can sit apart. fzf scores matches based on how tightly the characters cluster, whether they appear at word boundaries, and how close to the start of the string they sit.
None of that matters for daily use. But it explains why fzf feels so good: the best match almost always floats to the top.
Opening files in different programs
This is where fzf earns its keep in daily work. The command $(fzf) pattern works with anything.
Neovim and Vim
nvim $(fzf)That’s it. fzf shows your file tree, you type a few characters, hit enter, and you’re editing. No :e followed by tab-completing through nested directories. No :find with wildcards.
For filenames with spaces (they exist, and they’re annoying), the safer version is:
fzf --print0 | xargs -0 -o nvimmacOS apps
The open command on macOS pairs naturally with fzf:
open $(fzf)This opens the selected file in its default application. A .pdf opens in Preview. A .png opens in your image viewer. A .html opens in your browser.
Want a specific app? Use the -a flag:
open -a "Preview" $(fzf)
open -a "Visual Studio Code" $(fzf)
open -a "Figma" $(fzf)Any program
The pattern generalizes to everything:
cat $(fzf) # print file contents
code $(fzf) # open in VS Code
less $(fzf) # page through a file
cp $(fzf) ~/tmp/ # copy a file somewhereOnce you internalize $(fzf) as “let me pick a file,” you’ll start using it everywhere without thinking.
The preview window
fzf’s live preview earns its keep. When you’re scanning a list of similarly named files, seeing the contents helps you pick the right one.
fzf --preview 'cat {}'fzf replaces the {} placeholder with the currently highlighted item. As you move through the list, the preview updates in real time.
For syntax-highlighted previews, bat is a better choice than cat:
fzf --preview 'bat --color=always {}'I use this constantly. When I have six files named config.yaml in different directories, the preview tells me which one I want without opening each one.
How fzf fits inside editors
LazyVim and Neovim plugins
fzf’s influence goes beyond the shell. Inside Neovim, fuzzy finding has become the default way to navigate code.
LazyVim (a popular Neovim configuration) switched its default picker from Telescope to fzf-lua starting with version 14. The reason was speed. In large repositories, fzf-lua (which uses fzf’s matching algorithm) filters results noticeably faster than Telescope’s Lua-based matcher.
Inside LazyVim, pressing <leader>ff opens a fuzzy file finder that works the same way as fzf in the terminal. You type fragments, results narrow down, and you hit enter to open the file. The mental model is identical, just wrapped in a Neovim buffer instead of a terminal window.
This is fzf’s real legacy in the editor space: it normalized the idea that file navigation should be search-first, not tree-first. You shouldn’t need to browse a file explorer to find something. You should type what you remember and let the computer do the rest.
VS Code and other editors
VS Code’s Ctrl+P (or Cmd+P on macOS) is essentially the same concept. JetBrains has “Search Everywhere.” Emacs has Helm and Ivy. Every serious editor adopted fuzzy file finding because it works.
fzf didn’t invent fuzzy matching. But it made it a composable, standalone tool that works outside any single editor. That’s the difference. You learn one interaction pattern and it works in your shell, your editor, your scripts, and your aliases.
Shell integrations that matter
After installing fzf, you can enable shell key bindings that change how you use your terminal.
vim, hit Ctrl+T, pick a file, and the path appears right where your cursor was.history | grep, you type a few characters from any part of the command you remember.cd into it immediately. No more cd followed by tab-completing through five levels of nested folders.Ctrl+R alone is worth the install. I’ve wasted cumulative hours of my life scrolling through shell history looking for that one kubectl command I ran last week. With fzf, I type kube deploy prod and it finds it.
Building your own workflows
Because fzf is a filter, you can feed it anything. This is where it gets interesting.
Want to fuzzy-find a git branch and check it out?
git branch | fzf | xargs git checkoutWant to pick a running process and kill it?
ps aux | fzf | awk '{print $2}' | xargs killWant to browse your bookmarks and open one?
cat ~/.bookmarks | fzf | xargs openThe pattern stays the same. Generate a list, pipe it to fzf, do something with the selection. Once you see it, you start spotting opportunities for it everywhere.
Trade-offs and limitations
fzf is great, but it has limits.
Large result sets can overwhelm. If you run fzf at the root of a massive monorepo with hundreds of thousands of files, startup can be slow. You can mitigate this with
fd(a fasterfindalternative) as the input source, or by scoping your search to specific directories.Fuzzy matching can be too fuzzy. Sometimes you know the exact filename and
findorfdwith an exact match is faster. fzf’s strength is when you don’t remember the exact name.It’s interactive. fzf requires a human to make a selection. It’s poorly suited to scripted file operations where you need deterministic behavior.
Terminal-only. If your workflow lives entirely in a GUI, fzf won’t help much. It shines for people who spend real time in a terminal.
The core idea
fzf solves a problem so common that most people overlook it: the friction of specifying a file. Every time you cd, ls, tab-complete, or browse a file tree, you’re doing work that a fuzzy finder can eliminate.
The mental model is dead simple. fzf is a filter. It reads lines, you pick one, it outputs your choice. Plug it into any command, any workflow, any script. The $(fzf) pattern works everywhere because Unix pipes work everywhere.
Once you start using it, you’ll wonder how you tolerated the old way of finding files. I did.
Next steps
- Install fzf from the official repository and run the install script to enable shell key bindings.
- Try
nvim $(fzf)andopen $(fzf)to feel the difference in your own workflow. - Look into fd as a faster file-finding backend for fzf.
- If you use Neovim, explore fzf-lua or enable the fzf extra in LazyVim for in-editor fuzzy finding.
- Read about configuring my shell for more terminal productivity tips.
References
- fzf official repository, the source and documentation for fzf by Junegunn Choi.
- bat, a
catreplacement with syntax highlighting that pairs well with fzf’s preview feature. - fd, a fast, user-friendly alternative to
findthat works as an fzf input source. - LazyVim, a Neovim configuration that uses fzf-lua as its default fuzzy finder.
- Smith-Waterman algorithm, the bioinformatics algorithm that inspired fzf’s matching approach.

Comments #