# How to Implement Dynamic List Reloading in fzf: A Complete Guide

> Master dynamic list reloading in fzf using the reload action. This guide shows how to update your fzf candidate list seamlessly without restarting the UI.

- Repository: [Junegunn Choi/fzf](https://github.com/junegunn/fzf)
- Tags: how-to-guide
- Published: 2026-03-01

---

**You can implement dynamic list reloading in fzf by binding the `reload` or `reload-sync` action to events like `start`, `change`, or key presses, which executes a command and replaces the candidate list without restarting the UI.**

The `junegunn/fzf` command-line fuzzy finder supports dynamic list reloading, allowing you to update candidates on-the-fly based on user input or external events. This capability leverages the `reload` and `reload-sync` actions to execute arbitrary shell commands and refresh the internal list without disrupting the interactive interface.

## Understanding the reload Action Architecture

### Action Binding and Parsing

When you pass `--bind '<event>:reload:<command>'` on the command line, fzf parses this through `parseActionList` in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go) (lines 2030-2032). The parser identifies the action type as `actReload` or `actReloadSync` and stores the associated command string for later execution.

### Initial Reload on Start

If you bind the `reload` action to the `start` event, fzf handles this specially through `extractReloadOnStart` in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go) (lines 3895-3899). This function removes the binding from the keymap and returns the command string so it can execute before the UI appears. In [`src/core.go`](https://github.com/junegunn/fzf/blob/main/src/core.go) (lines 70-78), the initial reload runs while the terminal is being created, ensuring the list is populated immediately.

### Event Loop Execution

Inside the main event loop (`Terminal.run` in [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go)), every incoming key event translates into a list of actions. When the loop encounters `actReload` or `actReloadSync` (lines 7267-7284), it enters a specific branch that prepares the command for execution.

### Command Building and Placeholder Substitution

The terminal calls `t.buildPlusList(a.a, false)` to construct the final command string. This process handles placeholder substitution through `replacePlaceholder`, supporting:
- `{q}` – the current query string
- `{}` – the selected line

Temporary files may be created for multi-line selections, with cleanup handled via `defer removeFiles(temps)` in [`src/core.go`](https://github.com/junegunn/fzf/blob/main/src/core.go) (lines 78-81).

### Synchronous vs Asynchronous Reload

The implementation distinguishes between two modes:
- **`reload`** – Sets `reloadSync = false`. The command runs asynchronously, allowing the UI to remain responsive and accept further input while the list rebuilds.
- **`reload-sync`** – Sets `reloadSync = true` (line 7281 in [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go)). The UI blocks until the command finishes, ensuring the displayed list reflects the latest reload result before accepting subsequent actions.

## Practical Implementation Examples

### Reload on Start (Initial List Population)

Use the `start` event to populate the list when fzf launches:

```bash
fzf --bind 'start:reload:git ls-files' \
    --height 40% --layout reverse

```

This executes `git ls-files` immediately, filling the interface with repository files before user interaction begins.

### Reload on Query Change (Interactive Filtering)

Implement real-time search by binding to the `change` event:

```bash
RG_PREFIX='rg --column --line-number --no-heading --color=always --smart-case'
fzf --bind "start:reload:$RG_PREFIX ''" \
    --bind "change:reload:$RG_PREFIX {q} || true" \
    --ansi --disabled \
    --height 50% --layout reverse

```

Each keystroke triggers a new ripgrep search, with `{q}` substituted for the current query. The `|| true` ensures fzf continues running even if the search returns no results.

### Reload on Key Press (Manual Refresh)

Allow users to manually refresh the list with a keyboard shortcut:

```bash
fzf --bind 'ctrl-r:reload:ps -ef' \
    --header 'Press CTRL-R to reload' \
    --height 60% --layout reverse

```

Pressing `Ctrl-R` re-executes `ps -ef` and updates the process list dynamically.

### Synchronous Reload with reload-sync

Use `reload-sync` when the command must complete before further interaction:

```bash
fzf --bind 'ctrl-r:reload-sync:sudo apt update && apt list --upgradable' \
    --height 70% --layout reverse

```

The UI blocks until `apt update` finishes, ensuring the upgradable package list reflects the latest repository state.

## Key Source Files and Functions

| File | Role | Key Lines |
|------|------|-----------|
| [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go) | Parses command-line bindings and identifies reload actions via `parseActionList`; extracts initial reload commands through `extractReloadOnStart`. | 2030-2032, 3895-3899 |
| [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) | Implements the main event loop (`Terminal.run`) and handles `actReload`/`actReloadSync` execution, including command building with `buildPlusList` and placeholder substitution. | 7267-7284 |
| [`src/core.go`](https://github.com/junegunn/fzf/blob/main/src/core.go) | Manages initial reload execution during terminal creation and handles temporary file cleanup. | 70-78, 78-81 |

## Summary

- **Dynamic list reloading** in fzf uses the `reload` or `reload-sync` actions to execute commands and refresh candidates without restarting the UI.
- **Placeholder substitution** supports `{q}` for the current query and `{}` for selected items, processed through `replacePlaceholder` in the terminal.
- **Asynchronous reloading** (`reload`) keeps the UI responsive during command execution, while **synchronous reloading** (`reload-sync`) blocks until completion.
- **Event binding** options include `start` (initial load), `change` (query updates), and key presses for manual control.
- **Core implementation** spans [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go) for parsing, [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) for execution, and [`src/core.go`](https://github.com/junegunn/fzf/blob/main/src/core.go) for initialization.

## Frequently Asked Questions

### What is the difference between reload and reload-sync in fzf?

The `reload` action runs commands asynchronously, allowing you to continue typing or navigating while the list updates in the background. In contrast, `reload-sync` blocks the UI until the command completes, ensuring the candidate list reflects the latest state before accepting further input. According to the source code in [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) (line 7281), the only difference is the `reloadSync` boolean flag that determines whether the terminal waits for command completion.

### How do I pass the current query to the reload command?

Use the `{q}` placeholder in your command string. When the reload action triggers, fzf substitutes `{q}` with the current query text through the `replacePlaceholder` function. For example: `--bind "change:reload:rg --column --line-number {q}"` executes ripgrep with the typed query on every keystroke. This placeholder works with both `reload` and `reload-sync` actions.

### Can I use reload with multiple placeholder variables?

Yes, you can combine `{q}` (current query) with `{}` (selected line) and other placeholders in the same reload command. The `buildPlusList` function in [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) processes these placeholders before execution. For instance, `--bind "ctrl-e:reload:cat {} | grep {q}"` uses the selected file content and current query simultaneously. Note that using `{}` requires a selection to exist; otherwise, the placeholder expands to an empty string.

### Why does my reload action fail when the command returns no results?

fzf treats command failure (non-zero exit code) as an error condition that may interrupt the reload process. To prevent this, append `|| true` to your command, which ensures the exit code is always zero. For example: `--bind "change:reload:rg {q} || true"` allows fzf to continue operating even when ripgrep finds no matches. This pattern is particularly important for dynamic filtering where empty result sets are expected behavior.