# fzf User Guide

`fzf` is a general-purpose command-line **fuzzy finder** — an interactive
filter program for any kind of list. It implements a fuzzy matching algorithm
so that you can quickly type a pattern with omitted characters and still get
the result you want. It works as a shell binding for files, history, and
directories; as a finder inside Vim, tmux, and Zellij; and as a building
block for custom interactive scripts.

## 1. Quick Start

`fzf` reads lines from standard input, lets you filter them interactively
with a fuzzy match, and prints the selected line(s) to standard output. That
single contract is all you need to know to use it for almost anything.

### Key features

- Single self-contained Go binary, zero runtime dependencies, ships everywhere
  Go ships (Linux, macOS, Windows, FreeBSD, OpenBSD, Android, ARM/x86/RISC-V).
- Fuzzy matching with two algorithms (`v1` linear, `v2` Smith-Waterman) plus
  exact, prefix, suffix, equal, and exact-boundary matchers — all selectable
  via search syntax.
- Rich shell integration for Bash, Zsh, and Fish — file widget (CTRL-T),
  history widget (CTRL-R), directory widget (ALT-C), and `**` completion
  trigger.
- Configurable preview window with full ANSI color, scrolling, wrapping,
  and dynamic content via shell commands.
- 170+ built-in actions bindable to any key, mouse, or runtime event
  (`focus`, `change`, `load`, `start`, `result`, `resize`, `jump`, …).
- Live transformation primitives (`transform`, `bg-transform`, `reload`,
  `become`, `execute`) for building interactive multi-stage workflows
  inside a single fzf invocation.
- Optional HTTP server (`--listen`) for receiving actions from external
  processes — drives editor plugins and remote control workflows.
- Tmux popup mode (`--tmux`) and Zellij popup support, plus a separate
  `fzf-tmux` wrapper for environments that need it.

### Prerequisites

`fzf` is one statically linked binary. It needs a terminal that supports
xterm-style escape sequences. On Unix it works with any reasonable shell;
on Windows, the modern Windows Terminal, ConEmu, mintty, and WSL all work.

> [!TIP]
> On Linux and macOS, the fastest path is your package manager: `brew
> install fzf` on macOS, `sudo apt install fzf` on Debian/Ubuntu, `sudo dnf
> install fzf` on Fedora, `sudo pacman -S fzf` on Arch. On Windows, `choco
> install fzf` or `scoop install fzf`. The shell-integration scripts ship
> embedded in the binary and are emitted by `fzf --bash`, `fzf --zsh`, and
> `fzf --fish`.

### 30-second smoke test

```bash
# Pipe a list, filter interactively, print the chosen line:
ls | fzf
```

Type a few letters of any filename, press Enter, and that filename is printed
to stdout. The exit status reflects the outcome:

```text
0    selection accepted
1    no match (input was non-empty)
2    error
130  user pressed Ctrl-C / Esc / SIGINT
126  --bind become(...) replaced the process (this is the new program's exit, not fzf's)
```

### Capture the result and use it

```bash
file=$(ls | fzf) && echo "you picked: $file"
```

```text
you picked: README.md
```

### Where to go next

- **Set up shell integration** so CTRL-T, CTRL-R, and ALT-C work everywhere
  → see Section 2 (Installation) and Section 4 (Shell integration).
- **Add a preview window** to see file contents while you scroll → Section
  5 (Preview window).
- **Build interactive multi-stage scripts** with `--bind` → Section 7
  (Key bindings, actions, and events) and the Admin Guide for the full
  action catalog.

## 2. Installation

### 2.1 Choose an install method

`fzf` is distributed as: a single binary you can drop anywhere, a Homebrew
formula, packages in every major Linux/BSD distro, a Chocolatey/Scoop/winget
package on Windows, and a Vim plugin. Below are the canonical methods.

> [!NOTE]
> All methods install the same binary. The shell integration scripts are
> *embedded into the binary* — they print to stdout when you run
> `fzf --bash`, `fzf --zsh`, or `fzf --fish`. There is no separate
> "shell integration package" to install for any of them.

#### macOS (Homebrew)

```bash
brew install fzf
```

After installing, set up the key bindings (CTRL-T, CTRL-R, ALT-C) and shell
completion by sourcing the embedded scripts:

```bash
# Bash
eval "$(fzf --bash)"

# Zsh
source <(fzf --zsh)

# Fish
fzf --fish | source
```

Add the relevant line to your `~/.bashrc`, `~/.zshrc`, or
`~/.config/fish/config.fish` to load it automatically.

#### Debian / Ubuntu

```bash
sudo apt install fzf
```

Then enable bindings as above. Older Debian releases ship outdated `fzf`;
for the current release use the Homebrew-on-Linux method or the install
script (next section).

#### Fedora / RHEL

```bash
sudo dnf install fzf
```

#### Arch Linux

```bash
sudo pacman -S fzf
```

#### Windows (Chocolatey / Scoop / winget)

=== "Chocolatey"
    ```powershell
    choco install fzf
    ```

=== "Scoop"
    ```powershell
    scoop install fzf
    ```

=== "winget"
    ```powershell
    winget install fzf
    ```

#### Cross-platform install script (Git clone)

```bash
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
```

The installer asks whether to enable fuzzy auto-completion, key bindings,
and to update your shell rc files. It writes the embedded shell scripts to
`~/.fzf/shell/` and adds a single sourcing line to your rc.

> [!IMPORTANT]
> The Git-clone install method downloads a *prebuilt binary* matching your
> platform from the GitHub releases page. If your environment lacks
> internet access during install, you must build from source — see the
> Admin Guide, Building from Source.

#### Manual binary install

Download the matching archive from
`https://github.com/junegunn/fzf/releases`, extract the `fzf` (or
`fzf.exe`) binary, and put it somewhere on your `$PATH`. Then enable shell
integration with the `eval` / `source` lines above.

### 2.2 Verify the install

```bash
fzf --version
```

```text
0.71 (a1b2c3d)
```

The first token is the semver-style release; the parenthesised token is
the build commit (or `devel` for a development build).

```bash
echo "hello world" | fzf
```

Press Enter on the only line. fzf prints `hello world` and exits 0. If
that works, the binary is healthy.

> [!TIP]
> `fzf --man` opens the full manpage from the embedded copy — useful on
> Windows or any system where `man fzf` is unavailable.

### 2.3 Tmux popup mode (optional)

If you use tmux 3.3 or later, fzf can open in a floating popup:

```bash
echo -e "one\ntwo\nthree" | fzf --tmux
```

The `fzf-tmux` wrapper (a separate shell script that ships alongside the
binary in most distributions) provides finer-grained popup control on
older tmux versions. On modern tmux you should prefer `--tmux`.

> [!NOTE]
> `--tmux` and `--popup` are aliases for the same feature. Format:
> `--tmux=center,80%,40%` opens an 80%-wide, 40%-tall popup centered.
> Position keywords: `center`, `top`, `bottom`, `left`, `right`.

## 3. Configuration

`fzf` does not have a config file in the traditional sense. Instead, it
reads options from environment variables and from an optional defaults
file. This keeps the configuration model identical across shells.

### 3.1 The four cardinal environment variables

| Variable | Purpose |
|---|---|
| `FZF_DEFAULT_COMMAND` | Command to use when fzf's stdin is a TTY (no input piped). Typical value: a fast file finder like `fd --type f` or `rg --files`. |
| `FZF_DEFAULT_OPTS` | Default command-line options applied to every invocation (e.g. `--height 40% --layout=reverse --border`). |
| `FZF_DEFAULT_OPTS_FILE` | Path to a file containing default options (one per line, or `\`-continued). Read before `FZF_DEFAULT_OPTS`. |
| `FZF_API_KEY` | API key required by the HTTP server (`--listen`) when it binds to a non-local address. |

### 3.2 A starter `FZF_DEFAULT_OPTS`

```bash
export FZF_DEFAULT_OPTS='
  --height 40%
  --layout=reverse
  --border=rounded
  --preview="cat {}"
  --preview-window=right:60%:wrap
  --bind=ctrl-y:accept
  --color=fg:#d0d0d0,bg:#121212,hl:#5f87af
'
```

> [!TIP]
> Multi-line `FZF_DEFAULT_OPTS` values work because fzf splits options
> with shell-style word splitting. You can keep your config readable
> without backslash gymnastics.

### 3.3 Shell-integration environment variables

These tune the **shell widgets** (CTRL-T, CTRL-R, ALT-C) and the `**`
fuzzy completion trigger. They are read by the shell-integration scripts
emitted by `fzf --bash`, `fzf --zsh`, and `fzf --fish`.

| Variable | Default | Effect |
|---|---|---|
| `FZF_CTRL_T_COMMAND` | platform default walker | Command used by CTRL-T to enumerate files. |
| `FZF_CTRL_T_OPTS` | empty | Extra fzf options for CTRL-T. |
| `FZF_CTRL_R_OPTS` | empty | Extra fzf options for CTRL-R history. |
| `FZF_ALT_C_COMMAND` | platform default walker | Command used by ALT-C to enumerate directories. |
| `FZF_ALT_C_OPTS` | empty | Extra fzf options for ALT-C. |
| `FZF_COMPLETION_TRIGGER` | `**` | The trigger sequence for fuzzy completion. |
| `FZF_COMPLETION_OPTS` | empty | Extra fzf options for `**` completion. |
| `FZF_COMPLETION_PATH_OPTS` | empty | Extra path-completion fzf options. |
| `FZF_COMPLETION_DIR_OPTS` | empty | Extra directory-completion fzf options. |
| `FZF_TMUX` | `0` | Set non-zero to route the bindings through `fzf-tmux`. |
| `FZF_TMUX_HEIGHT` | `40%` | Tmux popup height for shell widgets. |
| `FZF_TMUX_OPTS` | empty | Extra `fzf-tmux` options. |

### 3.4 Disabling a single binding

To disable any of CTRL-T, CTRL-R, or ALT-C, set the corresponding
`*_COMMAND` variable to an empty string before sourcing the integration
script. See Section 4.

> [!NOTE]
> Full reference of every config option, every CLI flag, and the
> placeholder grammar lives in the Admin Guide, Section 3 (Configuration
> Reference).

## 4. Shell integration

Sourcing `fzf --bash` (or `--zsh` / `--fish`) installs three default key
bindings and one fuzzy completion trigger.

### 4.1 The three default key bindings

| Binding | Action |
|---|---|
| `CTRL-T` | Pick file(s) from the current directory tree and paste the path(s) onto the command line. |
| `CTRL-R` | Pick a command from shell history and paste it onto the command line. The exact command shows up in your prompt; press Enter to run, edit first, or Ctrl-C to discard. |
| `ALT-C` | Pick a directory from the current tree and `cd` into it. |

> [!TIP]
> `CTRL-R` honours your `HISTTIMEFORMAT` (Bash) or zsh history settings —
> duplicates are de-duplicated, and selecting an entry restores the cursor
> to the end of the line. Press `CTRL-R` again inside fzf to toggle
> sort-by-relevance vs sort-by-recency. Press `ALT-R` to toggle showing
> raw multi-line history entries.

### 4.2 The `**` completion trigger

After the integration script is sourced, type `**<TAB>` after most
commands to fuzzy-complete files, directories, processes, hostnames,
environment variables, or aliases.

```bash
vim **<TAB>          # fuzzy-pick a file
cd  **<TAB>          # fuzzy-pick a directory
kill -9 **<TAB>      # fuzzy-pick a running process
ssh **<TAB>          # fuzzy-pick a known hostname
unset **<TAB>        # fuzzy-pick an environment variable
unalias **<TAB>      # fuzzy-pick a defined alias
```

The trigger sequence is `**` by default; change it with
`FZF_COMPLETION_TRIGGER='~~'` (or whatever you like).

### 4.3 Per-command custom completion

The integration script ships built-in pickers for `cd`, `pushd`, `rmdir`,
`vi`, `vim`, `nvim`, `code`, `git`, `grep`, `rg`, `ls`, `cat`, `less`,
`ssh`, `kill`, and many others. To add your own:

```bash
# Bash
_fzf_setup_completion path tldr            # path completion for tldr
_fzf_setup_completion dir   cd cdr         # directory completion for two cmds
_fzf_setup_completion var   export unset   # variable completion
_fzf_setup_completion proc  pkill          # process completion
_fzf_setup_completion alias unalias        # alias completion
_fzf_setup_completion host  ssh sftp       # hostname completion
```

> [!NOTE]
> Custom commands also need an exec definition. To override how files are
> enumerated for the path completion of one command, define a function
> named `_fzf_complete_<cmd>()` that prints candidate lines on stdout
> and ends with `_fzf_complete "$@" -- "$@"`.

### 4.4 Disabling parts of the integration

```bash
# Disable just the CTRL-R history widget; keep CTRL-T and ALT-C
export FZF_CTRL_R_COMMAND=
eval "$(fzf --bash)"
```

> [!WARNING]
> Some Linux package builds split the integration into separate files
> under `/usr/share/fzf/`. Their content is the same as `fzf --bash`
> output, but they may lag the binary by a release. When in doubt,
> `eval "$(fzf --bash)"` is always current.

## 5. Preview window

The preview window shows command output beside the candidate list. It is
the single feature that turns fzf from a fuzzy filter into a complete
file/code/log browser.

### 5.1 Basic preview

```bash
ls | fzf --preview 'cat {}'
```

`{}` is the **current line placeholder**. fzf substitutes the current
selection into the command and re-runs it whenever the cursor moves.

> [!TIP]
> Use `bat`, `glow`, or `highlight` for syntax-coloured previews:
> `--preview 'bat --color=always --style=numbers {}'`.

### 5.2 Preview window layout

```bash
fzf --preview 'cat {}' --preview-window 'right:60%:wrap'
```

`--preview-window` accepts a comma-separated spec:

| Token | Meaning |
|---|---|
| `up` / `down` / `left` / `right` | Position relative to the list. |
| `<N>%` or `<N>` | Size as a percentage or absolute lines/columns. |
| `wrap` / `nowrap` | Wrap long lines (`wrap-word` for word boundaries). |
| `cycle` / `nocycle` | Cycle around when scrolling past edges. |
| `follow` / `nofollow` | Auto-scroll to the bottom when the preview command produces streaming output (e.g. `tail -f`). |
| `info` / `noinfo` | Show or hide the position indicator. |
| `hidden` / `nohidden` | Initially hidden; show with the `toggle-preview` action. |
| `border-rounded` / `border-sharp` / `border-bold` / `border-block` / `border-thinblock` / `border-double` / `border-horizontal` / `border-vertical` / `border-line` / `border-none` | Border style. Aliases: `rounded`, `sharp`, etc. |
| `+SCROLL[OFFSETS]` | Initial scroll position. `+{2}-/2` = jump to line 2 of `{2}`, then back-scroll half a screen. |
| `~HEADER_LINES` | Pin the first N lines of the preview as a header. |
| `<SIZE_THRESHOLD(ALT_LAYOUT)` | Use ALT_LAYOUT instead when the terminal is smaller than SIZE_THRESHOLD columns. |

Example with two layouts:

```bash
fzf --preview 'less -R {}' \
    --preview-window 'right,60%,border-rounded,<80(down,30%,border-horizontal)'
```

That spec says: by default, preview on the right with 60% width and a
rounded border; if the terminal is narrower than 80 columns, drop into a
bottom layout with 30% height and only top/bottom border lines.

### 5.3 Preview placeholders

| Placeholder | Substitutes |
|---|---|
| `{}` | The current line (no parsing). |
| `{1}`, `{2}`, … | Field N from the line, split by `--delimiter` (default: AWK). |
| `{1..3}`, `{2..}`, `{..-2}` | Field ranges. |
| `{q}` | The current query. |
| `{q:2}`, `{q:1..3}` | Field N (or field range) of the query, after delimiter splitting. |
| `{n}` | The zero-based item index. |
| `{+}` | All currently selected items (multi-select), space-separated. |
| `{*}` | All matching items in the current result. |
| `{f}` | Path to a temp file containing the current item (avoids quoting issues). |
| `{+f}` | Path to a temp file containing all selected items. |
| `{r}` | Raw line; do not shell-quote (advanced). |

> [!IMPORTANT]
> Placeholders are quoted using POSIX shell rules by default. If you need
> raw output (because your value already contains valid shell), use `{r}`
> or pipe to a sub-shell — but be aware of the injection surface. Never
> wrap an unsanitised user-controlled string in `--preview` or `become`.

### 5.4 Toggling, scrolling, refreshing preview

| Action | Description |
|---|---|
| `toggle-preview` | Show/hide the preview window. Bind to e.g. `--bind 'ctrl-/:toggle-preview'`. |
| `change-preview-window(<spec>)` | Cycle through preview specs (separate alternates with `\|`). |
| `preview-up` / `preview-down` | Scroll preview by one line. |
| `preview-page-up` / `preview-page-down` | Scroll by a full page. |
| `preview-half-page-up` / `preview-half-page-down` | Scroll by half a page. |
| `preview-top` / `preview-bottom` | Jump to start / end. |
| `refresh-preview` | Force re-running the preview command. |
| `change-preview(<command>)` | Replace the preview command at runtime. |
| `transform-preview-label(<command>)` | Recompute the preview label. |

A common preview-toggle binding:

```bash
fzf --preview 'cat {}' \
    --bind 'ctrl-/:toggle-preview,ctrl-u:preview-half-page-up,ctrl-d:preview-half-page-down'
```

## 6. Search syntax (extended-search mode)

By default, fzf is in **extended-search mode**: every space in your query
is an AND. Each token can carry a prefix or suffix that switches the
matcher type.

| Token | Match type | Example |
|---|---|---|
| `sbtrkt` | Fuzzy match | matches "Smith Brackets Trk T" |
| `'wild` | Exact-match (substring) | matches anything containing "wild" verbatim |
| `'wild'` | Exact-match (word boundary) | matches "wild" surrounded by non-word chars |
| `^music` | Prefix-exact match | matches lines starting with "music" |
| `.mp3$` | Suffix-exact match | matches lines ending with ".mp3" |
| `!fire` | Inverse-exact match | matches anything NOT containing "fire" |
| `!^music` | Inverse-prefix match | matches lines that do NOT start with "music" |
| `!.mp3$` | Inverse-suffix match | matches lines that do NOT end with ".mp3" |

Tokens separated by spaces are AND-ed. Tokens separated by `|` are OR-ed
(but only between consecutive tokens of the same group):

```text
^core go$ | rb$ | py$
```

Reads as: lines starting with `core` AND ending with one of `go`, `rb`,
or `py`.

> [!NOTE]
> `'` flips fuzziness. In default fuzzy mode, `'word` becomes exact; in
> exact mode (`-e` / `--exact`), `'word` becomes fuzzy. The double-quoted
> form `'word'` is *exact-boundary* — match `word` only between
> non-word characters.

### 6.1 Case sensitivity

| Flag | Behaviour |
|---|---|
| (default) `--smart-case` | Case-insensitive if your query is all lowercase, case-sensitive otherwise. |
| `-i`, `--ignore-case` | Always case-insensitive. |
| `+i`, `--no-ignore-case` | Always case-sensitive. |

### 6.2 Choosing the algorithm

| Flag | Effect |
|---|---|
| (default) v2 | Modified Smith-Waterman. Best match quality. Slightly slower per match but parallelised across cores. |
| v1 fallback | Linear-time fuzzy. Faster on huge inputs; less optimal scoring. fzf falls back to v1 automatically when input exceeds heuristic limits. |
| `--exact` / `-e` | Disable fuzziness — every token must appear verbatim. |
| `--no-extended` / `+x` | Disable the extended-search mode entirely; treat the query as a single fuzzy term. |

### 6.3 Sort and tiebreak

| Flag | Effect |
|---|---|
| `--no-sort` / `+s` | Preserve input order even after matching. |
| `--scheme=default` | Default scoring (good for general lists). |
| `--scheme=path` | Path-aware scoring: bonuses on path separators and at the start of basenames. |
| `--scheme=history` | History-aware scoring: bonuses for command-line patterns. |
| `--tiebreak=length,chunk,pathname,begin,end,index` | Up to 3 tiebreaks, comma-separated. `index` (insertion order) must be last when used. |

## 7. Key bindings, actions, and events

`--bind` is fzf's most powerful option. It maps **keys** or **events**
(window resize, query change, item focus, …) to one or more **actions**.

### 7.1 Bind syntax

```text
--bind '<trigger>:<action>[+<action>...][,<trigger>:<action>...]'
```

Triggers are keys (`ctrl-x`, `alt-up`, `f3`, `enter`, `tab`) or events
(`change`, `start`, `load`, `focus`, `result`, `resize`, `one`, `zero`,
`jump`, `jump-cancel`, `click-header`, `click-footer`, `multi`,
`backward-eof`).

Actions are strings drawn from the action catalog (see Admin Guide,
Section 3.4). Multiple actions chain with `+`. Multi-line action
arguments wrap in matching parens, brackets, or braces — fzf parses to
the matching close.

### 7.2 The four most useful action families

#### Execute — run a command without leaving fzf

```bash
fzf --bind 'ctrl-y:execute(echo {} | pbcopy)+abort'
```

`execute(...)` runs the command after fzf temporarily releases the TTY,
so interactive programs work correctly. `execute-silent(...)` keeps the
TTY held by fzf and discards stdout/stderr — better for noisy commands.

#### Become — replace fzf with a new program

```bash
fzf --bind 'enter:become(vim {})'
```

`become(...)` execs the given command in fzf's place (exit code 126
indicates a become happened). Use it when you want fzf to be a launcher
that vanishes once you've picked.

#### Reload — replace the candidate list

```bash
fzf --bind 'ctrl-r:reload(rg --files .)'
```

`reload(...)` re-runs the source command and replaces the input. Useful
for live filtering across multiple data sources.

#### Transform — rewrite fzf's own state on the fly

```bash
fzf --bind 'ctrl-t:transform-prompt(echo "{}> ")'
```

`transform-prompt`, `transform-query`, `transform-header`,
`transform-footer`, `transform-preview-label`, `transform-border-label`,
and friends let you swap a piece of the UI for the stdout of an arbitrary
command. The `bg-transform-*` variants run in the background so they
don't block the UI.

> [!TIP]
> When a command takes longer than a few hundred milliseconds, prefer
> `bg-transform-*` over `transform-*` — the UI stays responsive and the
> result is applied as soon as it's ready.

### 7.3 Events you can bind

| Event | Fires when … |
|---|---|
| `start` | fzf has finished startup. |
| `load` | All input has been read from stdin / the source command. |
| `focus` | The current item changes (cursor moves, list updates). |
| `change` | The query changes. |
| `result` | The result list updates. |
| `resize` | The terminal is resized. |
| `one` | The result count just became exactly 1. |
| `zero` | The result count just became 0. |
| `jump` | The user enters jump mode (default `ctrl-j`). |
| `jump-cancel` | Jump mode is cancelled without selection. |
| `click-header` / `click-footer` | A clickable header/footer field is clicked. |
| `multi` | The selection set changes. |
| `backward-eof` | Backspace is pressed when the query is already empty. |

### 7.4 Often-bound default keys (every line is bindable)

| Key | Default action |
|---|---|
| `enter` | accept |
| `ctrl-c` / `ctrl-g` / `esc` | abort |
| `ctrl-j` / `ctrl-n` / `down` | down |
| `ctrl-k` / `ctrl-p` / `up` | up |
| `tab` | toggle+down (multi-select) |
| `shift-tab` | toggle+up |
| `ctrl-a` | select-all (multi) |
| `ctrl-d` | deselect-all (multi) |
| `ctrl-r` | toggle-sort (within fzf — *not* the shell history widget) |
| `page-up` / `page-down` | page-up / page-down |
| `home` / `end` | beginning-of-line / end-of-line |

The full action catalog (170+ entries, including all `change-*`,
`transform-*`, `bg-transform-*`, `toggle-*`, preview, history, jump,
become/reload/execute, listen-server interactions) is enumerated in the
Admin Guide.

> [!WARNING]
> Some actions modify shell completion or terminal state in
> non-reversible ways inside `execute(...)`. If you need fzf to stay
> visually clean, prefer `execute-silent(...)` and write to a tempfile
> instead of inheriting the terminal.

## 8. Common tasks

The following recipes are battle-tested patterns drawn from the official
manpage and `ADVANCED.md`. Each one ends with the exact command you can
copy.

### 8.1 Open a file in your editor

```bash
fzf --bind 'enter:become(${EDITOR:-vim} {})'
```

Replaces fzf with your editor on the chosen file. Works because of
`become(...)`'s exec semantics.

### 8.2 Live ripgrep inside fzf

```bash
INITIAL_QUERY=""
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "

fzf --ansi --disabled --query "$INITIAL_QUERY" \
    --bind "start:reload:$RG_PREFIX {q}" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
    --bind 'enter:become(${EDITOR:-vim} +{2} {1})'
```

`--disabled` turns off fzf's own matching — every keystroke instead
re-runs ripgrep (`reload:`). The result: search code with regex power and
fzf's UX.

### 8.3 Pick from a long list with preview

```bash
ls | fzf --preview 'file {}; echo; head -50 {}' \
         --preview-window=down,50%,wrap
```

`file {}` shows the file type; `head -50 {}` shows a snippet. The
preview command is just shell — you can chain whatever you want.

### 8.4 Multi-select and operate on the selection

```bash
ls | fzf --multi \
         --bind 'ctrl-a:select-all,ctrl-d:deselect-all' \
         --bind 'enter:execute(rm -i {+})'
```

`{+}` expands to all selected items, space-separated and shell-quoted.
The interactive `rm -i` confirms each one before deletion.

### 8.5 Browse Git log with diff preview

```bash
git log --color=always --format="%C(auto)%h%d %s %C(black)%C(bold)%cr" |
fzf --ansi --no-sort \
    --preview 'git show --color=always {1}' \
    --preview-window=down,70%,wrap
```

### 8.6 Kill a process by fuzzy match

```bash
ps -ef | fzf --multi --header-lines=1 \
             --bind 'enter:execute(kill -9 {2})+abort'
```

`--header-lines=1` keeps the `ps` header always visible above the list.

### 8.7 Switch tmux session

```bash
tmux list-sessions -F '#S' | fzf --bind 'enter:execute(tmux switch-client -t {})+abort'
```

### 8.8 SSH to a known host

```bash
fzf < <(awk '/^Host / {print $2}' ~/.ssh/config) \
    --bind 'enter:become(ssh {})'
```

The shell integration's `**` trigger does this automatically:
`ssh **<TAB>`.

### 8.9 Browse and `cd` to a Git-tracked directory

```bash
git ls-files | xargs -n1 dirname | sort -u |
fzf --bind 'enter:become(cd {})'
```

(Chain with `pushd` or use `ALT-C` if you've enabled the integration.)

### 8.10 Build a typeahead picker around any command

```bash
seq 1 100 | fzf --print-query --bind 'enter:accept-or-print-query'
```

`accept-or-print-query` accepts the current item if there is one, or
prints the raw query to stdout when no candidate matches — turns fzf
into "pick an existing one OR enter a new value".

## 9. Troubleshooting

### Error: `unknown option: --foo`

**Cause:** the option is misspelt or your installed fzf is older than the
release that introduced it.

**Solution:**
1. Check `fzf --version` against `https://github.com/junegunn/fzf/releases`.
2. Run `fzf --help` to list all currently supported options.
3. Upgrade via your package manager.

**Prevention:** pin your tooling. fzf release notes flag breaking changes.

### Error: `unknown action: <name>`

**Cause:** an action used in `--bind` is misspelt or belongs to a newer
fzf release. Action names are kebab-case and the parser is strict.

**Solution:** check spelling against the Admin Guide action catalog, or
match the binding from `--help` output. Frequently-mistaken pairs:
`accept-non-empty` (not `accept-nonempty`), `clear-query` (not
`reset-query`), `toggle-preview-wrap` (not `toggle-wrap-preview`).

### Error: `bind action not specified: <key>`

**Cause:** `--bind 'ctrl-x'` with no `:action` part. fzf requires an
action for every bound key.

**Solution:** add an action: `--bind 'ctrl-x:abort'`. To unbind a default
key, bind it to `ignore`: `--bind 'ctrl-x:ignore'`.

### Error: `template should include at least 1 placeholder: ...`

**Cause:** A `--header`, `--prompt`, `--ghost`, `--info-command`, or
`--preview` value uses a templated form (`{...}`) that has no
placeholders. fzf rejects it because the output would be static.

**Solution:** either include `{}`/`{1}`/`{q}` in the template, or use the
non-template variant (`--prompt 'literal> '` instead of
`--transform-prompt`).

### Error: `FZF_API_KEY is required to allow remote access`

**Cause:** `--listen` was given a non-localhost bind (e.g.
`--listen 0.0.0.0:6266`) but `FZF_API_KEY` is not set.

**Solution:**
```bash
export FZF_API_KEY=$(openssl rand -hex 16)
fzf --listen 0.0.0.0:6266
```

**Prevention:** if you don't actually need remote access, use plain
`--listen 6266` (binds to localhost) or `--listen ./fzf.sock` (Unix
socket). For remote use, the API key is mandatory.

### Error: `socket already in use: <path>`

**Cause:** a previous fzf instance was killed without unlinking its Unix
socket file.

**Solution:** `rm <path>` and retry. If multiple users share the same
path, switch to per-user paths or use TCP.

### Error: `invalid color specification: <spec>`

**Cause:** `--color=…` value is malformed. The grammar is comma-separated
`group:fg,bg,attr` triples (each part optional), where `group` is one of
`fg`, `bg`, `hl`, `pointer`, `marker`, `prompt`, `info`, `border`,
`spinner`, `header`, `gutter`, `selected-fg`, `selected-bg`, `selected-hl`,
`preview-fg`, `preview-bg`, `nth`, `label`, `separator`, `scrollbar`, etc.

**Solution:** start from a base scheme (`--color=dark`, `--color=light`,
`--color=base16`, `--color=bw`) and override specific groups:
`--color=dark,fg:#cccccc,hl:bright-yellow,marker:cyan:bold`.

### Issue: shell integration "feels broken" after upgrading fzf

**Cause:** old `~/.fzf.bash` / `~/.fzf.zsh` / `~/.fzf.fish` files
generated by an older installer no longer match the binary.

**Solution:** regenerate by sourcing the embedded scripts directly:

```bash
# ~/.bashrc:  drop any old `[ -f ~/.fzf.bash ] && source ...` line
# and replace with:
eval "$(fzf --bash)"
```

The embedded scripts are always in lock-step with the binary.

### Issue: `**<TAB>` does nothing

**Cause:** the completion script wasn't sourced for your shell, OR
another tool is already binding `**`.

**Solution:**
1. Confirm: `type _fzf_complete` should print a function definition.
2. If no, source the integration: `eval "$(fzf --bash)"`.
3. If yes, change the trigger: `export FZF_COMPLETION_TRIGGER=';;'`.

### Issue: preview window is blank

**Cause:** the preview command produced no output, or the placeholder
expanded to nothing.

**Solution:**
1. Run the preview command manually with a known input to verify it works.
2. Check the placeholder. `{}` always works; `{1}` requires a delimiter.
3. Add `bash -c 'set -x; cat {}'` and watch the preview window.

### Issue: very slow startup on macOS

**Cause:** built-in macOS `awk` chokes on non-UTF-8 input from the
shell-integration scripts.

**Solution:** the scripts already detect this and prefer `mawk` / `gawk`
when available. Install one: `brew install mawk` or `brew install gawk`.

### Issue: history widget (CTRL-R) shows duplicates

**Cause:** Bash history is loaded from disk and the in-memory history
both — and de-duplication in CTRL-R only de-dups exact matches.

**Solution:** add `HISTCONTROL=ignoredups:erasedups` and `shopt -s
histappend` to `~/.bashrc`.

### Issue: ALT-C doesn't change directory

**Cause:** Some terminals (notably macOS Terminal.app's default profile)
don't send the `Esc` byte for ALT keys. fzf binds `\ec` and never sees
the modifier.

**Solution:** in Terminal.app preferences, enable "Use Option as Meta
key". For iTerm2, set the Option key behaviour to "Esc+".

> [!CAUTION]
> Never run `fzf --listen-unsafe` on a multi-user machine or any
> non-trusted network. The unsafe variant skips API-key authentication and
> exposes every fzf action — including `become`/`execute`/`reload` — to
> any process that can reach the listening port.

## 10. FAQ

**Q: Can I use fzf without sourcing the shell integration?**

Absolutely. fzf is a normal Unix filter — pipe in, pipe out. The shell
integration is convenience for CTRL-T / CTRL-R / ALT-C; it's optional.

**Q: How do I tell fzf to use `fd` or `rg` for the file walker?**

Set `FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'`
(or `rg --files --hidden --follow --glob '!.git/'`). fzf uses this when
its stdin is a TTY. For the CTRL-T widget, use
`FZF_CTRL_T_COMMAND='fd ...'`.

**Q: Does fzf work on Windows without WSL?**

Yes. Native Windows builds ship as `fzf.exe`. They work in modern Windows
Terminal, ConEmu, mintty, and WSL. The shell integration scripts work
under Git Bash, MSYS2, and Cygwin; PowerShell support is community-driven
via `PSFzf`.

**Q: What's the difference between `--multi` and `--multi=N`?**

`--multi` (or `-m`) enables multi-select with no upper bound (capped at
INT_MAX). `--multi=3` caps the selection set at 3 items — once reached,
further `tab` is a no-op until something is deselected.

**Q: How do I make CTRL-R show the most recent commands first?**

Inside the history widget, `CTRL-R` toggles between sort-by-relevance
(default) and sort-by-recency. To make recency the default, add
`--bind=ctrl-r:toggle-sort` to `FZF_CTRL_R_OPTS`.

**Q: Can I have a different preview command per file type?**

Yes — use a wrapper script that dispatches by extension:

```bash
fzf --preview 'preview-router {}'
```

where `preview-router` shells out to `bat` for code, `mediainfo` for
videos, `exiftool` for images, etc.

**Q: How do I disable colour entirely?**

`fzf --no-color` for one invocation, or set `NO_COLOR=1` (the
[no-color.org](https://no-color.org/) standard) for all of them. Add
`--color=bw` if you also want a black-and-white reverse-video selector.

**Q: Why does my custom binding not fire?**

Most often: the action name is wrong, OR another binding consumes the
key first. Try `--bind '?:show-input+ignore'` to dump the current
input state and keys (it's a debug nudge, not a feature).

**Q: Does fzf send any data anywhere?**

No. fzf is fully offline — no telemetry, no update checks, no network
activity. The HTTP server (`--listen`) only opens when you explicitly
request it.

**Q: Where do I report bugs or request features?**

`https://github.com/junegunn/fzf/issues`. Before opening, run
`fzf --version` and include the output, your shell, your OS, and a
minimal reproducing command.

**Q: How do I bind a key to clear the query?**

```bash
fzf --bind 'ctrl-l:clear-query'
```

**Q: What does the exit code 130 mean?**

The user pressed Ctrl-C / Esc / SIGINT. This is the standard convention
for "user interrupted" inherited from POSIX `kill(SIGINT) -> 128 + 2`.

**Q: Does fzf integrate with Vim?**

Yes — the `plugin/fzf.vim` shipped in the repo is a thin Vim/Neovim
plugin around the binary, providing the `:FZF` command. The full-fat
plugin with `:Files`, `:Buffers`, `:Rg`, etc. is the separate
`junegunn/fzf.vim` repository.

---

_Generated April 2026 from junegunn/fzf at 06e7057. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
