# fzf.fish: fzf key bindings for the Fish shell

> fzf.fish wires fzf into Fish with six mnemonic key bindings for files, git objects, history, processes and variables. It is a small, opinionated plugin with a hard dependency list and a Fisher-only support policy.

**PatrickF1/fzf.fish** — 🔍🐟 Fzf plugin for Fish

- Repository: https://github.com/PatrickF1/fzf.fish
- Stars: 2,698 · Forks: 106
- Language: Shell
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/patrickf1-fzf-fish

## The problem fzf.fish solves on a Fish command line

Fish ships with its own history search, tab completion and a pager, but it does not ship a fuzzy finder, and it does not give you a single keystroke that turns a file path, a commit hash or a process id into text on the current command line. That is the gap this plugin fills. It is aimed at people who already use fzf and want the same fuzzy selection behaviour applied to the specific entities a shell session deals with: files in the current directory, commits in the current repository, the working tree status, command history, running processes and shell variables.

The design decision worth noting is that fzf.fish does not introduce a new command you type. It binds keys. The README describes the bindings as mnemonic, and the mapping is deliberate: Ctrl+Alt+F for file, Ctrl+Alt+L for log, Ctrl+Alt+S for status, Ctrl+R for reverse-i-search history, Ctrl+Alt+P for process, Ctrl+V for variable. That choice means the plugin is invisible until you press something, which is good for people who dislike extra commands in their namespace, and awkward for people who prefer to script their shell rather than bind it.

## How the searches work: fzf input, output and preview

Each command is a pipeline with three documented parts: what is fed to fzf, what is inserted back, and what the preview window shows. Search Directory feeds fzf a recursive listing of the current directory's non-hidden files and returns relative paths; the README states it ignores files that are also ignored by git, which is done through fd. Directories come back with a trailing slash, so selecting exactly one directory and pressing ENTER lets you cd into it immediately. If the token under the cursor is already a directory with a trailing slash, that directory is searched instead of the current one. The preview window shows the file with syntax highlighting, a directory listing, or the file type.

Search Git Log feeds a formatted git log and returns hashes, previewing the commit message and diff. Search Git Status feeds git status and returns relative paths of the selected lines, previewing the git diff of the file. Search History feeds Fish's command history and returns selected commands, previewing the whole command with Fish syntax highlighting. Search Processes feeds the pid and command of all running processes as output by ps and returns pids, previewing CPU usage, memory usage and start time. Search Variables feeds all shell variables currently in scope and returns the selected variables, previewing scope info and values. One documented exclusion: $history is left out of Search Variables for technical reasons, and the README points you at Search History instead.

Two behaviours apply across all six. Tab selects multiple entries. And if you trigger a search while the cursor sits on a word, that word seeds the fzf query and is replaced by whatever you select, which is the detail that makes the bindings feel like completion rather than a separate tool.

## Installing fzf.fish and running your first search

The README lists four CLI dependencies with minimum versions: fish 4.0.0, fzf 0.33.0, fd 8.5.0 and bat 0.16.0. The last two are only needed for Search Directory, so a user who wants git and history search alone can skip them. Check what you have before installing, because the plugin does not vendor or bundle any of these.

## Configuration: bindings, fzf options and preview commands

Key bindings are not edited by hand. The plugin ships a helper whose documentation you can read in the shell:

```fish
fzf_configure_bindings --help
```

Calling fzf_configure_bindings from config.fish is how the README says to persist custom bindings. That is a cleaner arrangement than most shell plugins, which expect you to write bind calls yourself and remember the exact function names.

fzf options are handled at two levels. Globally, fzf reads FZF_DEFAULT_OPTS and FZF_DEFAULT_OPTS_FILE; if neither is set, fzf.fish supplies its own default options whenever it runs fzf. Per command, there is one variable each: fzf_directory_opts, fzf_git_log_opts, fzf_git_status_opts, fzf_history_opts, fzf_processes_opts and fzf_variables_opts. The value is appended last to the option list, and because fzf takes the last instance of a repeated option, your settings win. The README suggests this as the route to adding fzf key bindings that open files in Vim, adjusting the preview command or window, re-populating the input list on demand, and changing the search mode.

Preview commands for Search Directory are separate variables. Directories preview with ls by default and regular files with bat. To change them, set the command without the target path, since the plugin supplies the argument itself. The README gives these two examples:

```fish
set fzf_preview_dir_cmd eza --all --color=always
set fzf_preview_file_cmd cat -n
```

The README also points to a Cookbook wiki page for further ideas, and mentions passing custom options to fd to change which files Search Directory lists.

## Where fzf.fish gets in your way

The most concrete limitation is stated plainly in the installation section: fzf.fish is incompatible with other fzf plugins, and the README links to a wiki page on uninstalling two common alternatives before you install. If you are attached to another fzf integration, this is not a plugin you can layer on top of it. You either commit to fzf.fish or you keep what you have.

Support is narrow in the same way. The README says the plugin can be installed manually or with other plugin managers but that only Fisher is officially supported, so an oh-my-fish user is on an unsupported path even though the topic list suggests the plugin is packaged for it. The dependency floor is also not trivial: fish 4.0.0 is a recent major version, and anyone on an older Fish cannot use the plugin at all. Search Directory additionally requires fd 8.5.0 and bat 0.16.0, and while the plugin will function without them, that is the search most people reach for first.

Finally, the whole model is interactive. There is no documented non-interactive entry point for piping a selection into a script, and $history is deliberately excluded from Search Variables. If you need a fuzzy picker inside a shell script or a CI job, this is the wrong tool; call fzf directly.

## Alternatives and how they differ

The obvious alternative is using fzf on its own. fzf ships shell integration for several shells, and the difference is scope: plain fzf gives you a generic picker and a completion-style binding, and you supply the input command and the action for each case. fzf.fish supplies six fixed pipelines with chosen inputs, chosen outputs and chosen previews, plus a helper for rebinding them. You trade flexibility for not having to write the pipelines. If you have already built your own fzf functions for git and files, fzf.fish duplicates work you have done.

A second comparison is Fish's built-in Ctrl+R history search, which fzf.fish replaces with a fuzzy, previewed version. If you are happy with Fish's native search, the history binding alone is not a reason to install anything.

On the question that often comes up alongside fzf, ripgrep and fzf are not substitutes. ripgrep searches file contents and returns matches; fzf filters a list of candidates interactively and returns selections. fzf.fish uses neither for content search: Search Directory lists paths, and the preview is where you see file contents, through bat or whatever you set in fzf_preview_file_cmd.

## Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-06-18. Releases are infrequent rather than steady: v11.0 on 2026-03-10, v10.3 on 2024-04-07, and v10.2 on 2023-11-06. That cadence matters if you pin versions, because a major release can arrive after a long quiet period, and the jump from v10.3 to v11.0 came roughly two years after the previous release. Budget for reading release notes before upgrading rather than assuming patch-level continuity.

The licence is MIT, which permits commercial and private use with the usual requirement to keep the copyright and permission notice. That is a statement about the licence text, not legal advice; check LICENSE.md in the repository for the exact terms and your own obligations.

The upgrade cost is mostly in configuration you have written. Custom bindings set through fzf_configure_bindings and options set in the six per-command variables are your code, not the plugin's, and a major release is where those are most likely to need attention. The README does not document rollback or a downgrade procedure, so keep a known-good config.fish if you upgrade on a machine you depend on.

## Conclusion

Adopt fzf.fish if you already live in Fish, already have fzf installed, and want file, git and history search behind fixed mnemonics instead of memorised fzf invocations. Do not adopt it if you are on another shell, if you want to keep a different fzf plugin loaded, or if you cannot install fd and bat, since Search Directory depends on both. Before installing, check your fish version with fish --version against the 4.0.0 minimum, confirm fzf is at least 0.33.0, and read the wiki page on uninstalling other fzf plugins, because the README states the plugin is incompatible with them. Then run fisher install PatrickF1/fzf.fish and test Ctrl+Alt+F in a git repository before you customise anything.

## FAQ

### How do I install fzf.fish?

Install fish 4.0.0, fzf 0.33.0 and, for Search Directory, fd 8.5.0 and bat 0.16.0, remove any other fzf plugins, then run fisher install PatrickF1/fzf.fish. The README states that manual installation and other plugin managers work but only Fisher is officially supported.

### How do I use fzf to find files with fzf.fish?

Press Ctrl+Alt+F to trigger Search Directory. fzf receives a recursive listing of the current directory's non-hidden files, and the relative path of your selection is inserted into the command line; directories come back with a trailing slash so you can cd into a single selection immediately.

### What are the key bindings in fzf.fish?

Ctrl+Alt+F for Search Directory, Ctrl+Alt+L for Search Git Log, Ctrl+Alt+S for Search Git Status, Ctrl+R for Search History, Ctrl+Alt+P for Search Processes and Ctrl+V for Search Variables. They can be changed with the fzf_configure_bindings helper, called from config.fish to persist.

### Is fzf.fish compatible with other fzf plugins?

No. The README states the plugin is incompatible with other fzf plugins and links to a wiki page on uninstalling two common alternatives before you install it.

### How do I change the fzf options fzf.fish uses?

Set FZF_DEFAULT_OPTS or FZF_DEFAULT_OPTS_FILE to change options globally; if neither is set, the plugin supplies its own defaults. For one command, set the matching variable such as fzf_history_opts or fzf_directory_opts, whose value is appended last and therefore takes precedence.

## Sources

- [Issues](https://github.com/PatrickF1/fzf.fish/issues)
- [License: MIT](https://github.com/PatrickF1/fzf.fish/blob/main/LICENSE)
- [PatrickF1/fzf.fish on GitHub](https://github.com/PatrickF1/fzf.fish)
- [README](https://github.com/PatrickF1/fzf.fish/blob/main/README.md)
- [Releases](https://github.com/PatrickF1/fzf.fish/releases)

---

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