# fzf installs as a binary, but the part you actually use comes from a shell script

> Fourteen package managers will put the fzf binary on your machine and none of them will give you the key bindings, which arrive from a separate install script. The Go dependency list is six modules, and the build refuses to run outside a git checkout.

**junegunn/fzf** — fzf is an interactive command-line fuzzy finder that filters files, command history, processes, and other text streams.

- Repository: https://github.com/junegunn/fzf
- Website: https://junegunn.github.io/fzf/
- Stars: 83,244 · Forks: 3,605
- Language: Go
- License: MIT
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/junegunn-fzf

## Fourteen package managers install the binary and none of them wires up the bindings

The installation table is long, and reading it closely explains most of what fzf is. On macOS and Linux there is Homebrew, MacPorts and mise. The Linux table covers fourteen more, one per distribution or toolchain, from Alpine's apk through Debian and Ubuntu's apt, Fedora's dnf, Arch's pacman, Void's xbps-install, openSUSE's zypper, Gentoo's emerge, FreeBSD's pkg, NetBSD's pkgin, OpenBSD's pkg_add, NixOS, Conda, Spack and Alpine again.

```sh
brew install fzf
```

```sh
mise use -g fzf@latest
```

Windows has four: Chocolatey, Scoop, Winget and MSYS2, where the command is pacman with the MSYS2 prefix variable in it.

The same IMPORTANT note appears twice, once after the Homebrew section and once after the Linux table, and it says the same thing both times: to set up shell integration for key bindings and fuzzy completion, see the instructions further down. The project is telling you, in the two places you are most likely to stop reading, that the package manager got you a file and not a workflow. The breadth of the table is real, but it is breadth of distribution, not of setup.

## The install script, not the package manager, is what makes CTRL-R and completion appear

The repository root tells you what the missing half is. Alongside the Go source there is an install script, a matching install.ps1 for Windows, an uninstall script, and four directories that are not Go code at all: bin/, shell/, plugin/ and doc/.

The container build shows the two steps as separate commands:

```sh
make install && ./install --all
```

The `--all` flag is doing the work the package managers skip. Without it you get the binary and nothing else; with it the shell key bindings and the completion setup are written into your shell configuration.

The consequence for a new user is a quiet failure. The command is installed, `fzf` runs, and pressing the key you saw in a screenshot does nothing, because nothing registered a binding. The README does not print an error in that situation, it simply has not been read far enough. Diagnosing that costs more time than running the install script correctly would have.

## The reload bindings turn fzf into a process manager, and that is a different tool

The advanced topics include a preview window and a set of actions for reloading the candidate list while fzf is running. Three of them are named in the section headings: pressing CTRL-R updates the list of processes, CTRL-D or CTRL-F switches between sources, and there is an interactive ripgrep integration.

That makes fzf something other than a file picker. The same binary that filters a directory listing will, with the right source, filter the running processes on the machine, and the source can be swapped without leaving the interface.

What that costs is predictability. A fuzzy matcher over filenames has a correct answer for almost every query, since a path either matches or it does not. A fuzzy matcher over process IDs will happily return a plausible-looking process that is not the one you meant, and the interface is designed to make the wrong one easy to pick because it is the closest string. The bindings that make fzf powerful for process work are the same ones that make it dangerous for it, and nothing in the interface distinguishes the two cases.

## Height mode and popup mode decide whether fzf owns the screen or borrows part of it

The display modes are the part of fzf that changes how a script feels, and there are two named ones, a height mode and a popup mode. The height mode constrains the finder to a fixed number of lines; the popup mode places it in a floating window.

This is a small feature with a large effect on whether fzf can be dropped into an existing workflow. A full-screen finder interrupts scrollback and needs the terminal to itself. A constrained one sits under your prompt, takes a fixed number of rows, and can appear in the middle of a command you are already typing without destroying the history above it.

The limitation is that neither mode changes what fzf matches, only how it is drawn. If the underlying list is wrong, a prettier presentation makes the wrong answer easier to click. And because these are options rather than defaults, a script that does not pass them gets whichever behaviour the installed version defaults to, so a fzf upgrade can change the look of a command you did not touch.

## Six Go modules, and the matching algorithm is not one of them

The dependency list in go.mod is short enough to read in full: fastwalk for fast directory traversal, tcell for terminal handling, go-shellwords for shell word parsing, go-isatty for terminal detection, uniseg for grapheme cluster segmentation, and golang.org/x/term plus golang.org/x/sys for terminal control and system calls. Two more, go-colorful and go-runewidth, come in as indirect.

The module targets Go 1.23.0.

Worth pausing on what is absent. There is no fuzzy matching library, no indexing library, no configuration parser. The matching that the tool is named for is in this repository, and the module targets say it is written rather than delegated.

The practical consequence is small but real for anyone extending it. The uniseg dependency handles grapheme clusters, which is why combining characters and emoji in filenames do not break the ranking, and that is the kind of detail you would otherwise reimplement incorrectly. Everything else, the scoring, the ranking, the tie-breaking, is code you will have to read in src/ before you can predict how a query will order a result set.

## The Makefile errors out when it cannot read a version from git

The build refuses to guess. If FZF_VERSION is not set, the Makefile falls back to git describe, and if that comes back empty the build stops with an error saying it is not on a git repository and cannot determine the version. The same pattern repeats for FZF_REVISION, which falls back to git log against the source list.

```make
$(error Not on git repository; cannot determine $$FZF_VERSION)
```

Both can be overridden by exporting FZF_VERSION and FZF_REVISION, which is the supported path for building from a source archive with no history.

This is a deliberate choice rather than an oversight, since the version string is compiled into the binary through ldflags along with a short revision hash, and a binary reporting an unknown version is worse than one that refuses to build. The cost lands on anyone building from a tarball, or in a build system that strips the .git directory to shrink a context. That build fails at the Makefile with a message about git, which is accurate and not immediately obvious as the real cause.

## The test suite runs in a Ruby container with tmux, not on your machine

The Dockerfile is a test environment rather than a distribution image. It starts from a Ruby base image, installs git, make, go, zsh, fish and tmux, then adds Nushell from its own apt repository. A specific minitest version is installed as a gem. The final command opens a tmux session that runs the Ruby test runner with pipefail enabled and writes a marker file on success.

Every shell fzf claims to support is therefore present in one image, and the suite is checking behaviour across shells rather than across operating systems.

That design has a consequence for anyone trying to reproduce a failure. A bug in the bash key bindings and a bug in the fish key bindings look identical from the outside, and telling them apart means reproducing the shell as well as the version. The image is the only configuration where all of them are guaranteed present, so local debugging of a shell-specific problem means rebuilding that environment rather than switching terminals.

## Conclusion

Take fzf if you spend your day in a terminal and are willing to source one script, because the value is in the bindings and the completion, not in the binary. Skip it if you want a graphical picker or a Windows-first workflow, where the MSYS2 route is a pacman package with a prefix variable rather than a first-class installer. Before you wire it into anything, run the install script rather than a package manager, and check the key bindings file afterwards, since the two install paths produce a different result and the package manager one leaves you with a command that does nothing until you configure it yourself.

## FAQ

### What is fzf used for?

fzf is a general-purpose command-line fuzzy finder and interactive terminal toolkit. It filters files, command history, processes and other text streams, and its shell integration adds key bindings and fuzzy completion to your prompt.

### How do I use fzf on Windows?

fzf is packaged for Windows through Chocolatey, Scoop, Winget and MSYS2. The MSYS2 route installs with pacman using the MSYS2 package prefix variable, and the repository also ships an install.ps1 script alongside the shell install script.

### How do I use fzf to find files?

You pipe a list of paths into fzf and it filters them as you type, selecting the closest match. The Makefile pulls in fastwalk for fast directory traversal, and an advanced topic covers respecting .gitignore when walking a tree.

### How to use fzf in terminal?

Install it with a package manager, then run the repository's install script with the --all flag so the shell key bindings and fuzzy completion are configured. The package manager alone gives you the binary without the bindings.

### what is junegunn fzf

It is a Go implementation of a fuzzy finder, MIT licensed and distributed as a single binary for Linux, macOS, Windows, FreeBSD, NetBSD, OpenBSD and NixOS. The default branch is master and the newest release is 0.74.4.

## Sources

- [Official documentation](https://junegunn.github.io/fzf/)
- [Official README](https://github.com/junegunn/fzf#readme)
- [Project repository](https://github.com/junegunn/fzf)
- [Release notes](https://github.com/junegunn/fzf/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/junegunn-fzf
