# fzf `--sync` Option Explained: When to Use Synchronous Search

> Unlock efficient multi-stage filtering with fzf --sync search. Learn when to use this blocking option for deterministic execution and ensure your workflows run perfectly.

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

---

**The `--sync` flag forces fzf to perform a blocking search that waits for the initial filtering and any bound startup actions to complete before rendering the UI, ensuring deterministic execution for multi-stage filtering workflows.**

The `junegunn/fzf` command-line fuzzy finder operates asynchronously by default, displaying the interface immediately and filtering results as you type. However, certain scripting scenarios require the fzf `--sync option to defer UI activation until specific preprocessing steps or side-effects finish executing.

## What Does the fzf `--sync` Option Do?

By default, fzf initializes the terminal interface immediately and processes input asynchronously. When you enable the `--sync` flag, fzf switches to **synchronous search mode**, blocking the UI until two conditions are met:

1. The initial filtering of the input list completes.
2. Any actions bound to the `start`, `load`, `result`, or `focus` events finish executing.

According to the source code in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go), the flag is defined as "Synchronous search for multi-staged filtering" at line 60. This mode is particularly relevant when chaining fzf with preprocessing commands or when using the `--listen` server mode where deterministic state matters.

## How Synchronous Search Works Under the Hood

### Command-Line Parsing

In [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go), the `--sync` boolean flag is parsed alongside other options. When set, it populates a configuration field that propagates to the terminal runner, signaling that the standard async initialization should be bypassed.

### Deferred UI Activation

The core implementation resides in [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go). Normally, fzf activates the terminal UI on the first available tick. However, when `--sync` is enabled, the code explicitly defers UI activation:

- The interface is suppressed during the initial filter pass.
- UI rendering occurs only on the *second* tick, after [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) lines 1736 and 5947 confirm that the initial filtering and event bindings have settled.

This deferred activation prevents the visual flicker that occurs when large datasets load or when heavy preprocessing delays the initial candidate list.

### Event Binding Guarantees

Synchronous mode ensures that side-effects complete before user interaction begins. For example, if you bind an action to the `focus` event but no items exist to focus, [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) at line 1874 contains logic to activate the UI regardless, ensuring the `--sync` contract is honored even in edge cases.

## When Should You Use the `--sync` Flag?

Use the fzf `--sync` option in the following scenarios:

- **Multi-stage filtering workflows** – When you need to run preprocessing commands (e.g., fetching data from a remote server, parsing large logs) before the user sees the candidate list.
- **Avoiding UI flicker with `--listen`** – When running fzf as a server (`--listen`), synchronous mode prevents the interface from appearing before the initial data load completes, creating a cleaner user experience.
- **Guaranteeing side-effect order** – When scripts depend on actions bound to `start`, `load`, `result`, or `focus` events completing before the UI renders, preventing race conditions in automation workflows.

Avoid using `--sync` for simple, local file filtering where immediate feedback is preferred, as the blocking behavior adds latency before the interface appears.

## Practical Examples of fzf Synchronous Search

The following examples demonstrate patterns found in the fzf test suite ([`test/test_core.rb`](https://github.com/junegunn/fzf/blob/main/test/test_core.rb) lines 1346-2122 and [`test/test_server.rb`](https://github.com/junegunn/fzf/blob/main/test/test_server.rb) line 9):

```bash

# Example 1: Preprocessing with start event binding

seq 1000 | fzf --sync \
    --bind 'start:execute-silent:echo "preload complete" > /tmp/status' \
    --preview 'head -n 5 {}'

```

```bash

# Example 2: Server mode with synchronous initialization

fzf --listen 6266 --sync \
    --bind 'start:up,load:up,result:up,focus:change-header:Ready' \
    --query "initial"

```

```bash

# Example 3: Multi-select with guaranteed event completion

seq 100 | fzf --multi --sync \
    --bind 'start:select-all+last+preview(echo welcome)'

```

```bash

# Example 4: Accept-nth with delimiter and synchronous accept

echo "foo,bar,baz" | fzf -d, --accept-nth 2,2 --sync \
    --bind 'start:accept' > selected.txt

```

## Summary

- The **`--sync` option** enables synchronous search mode in fzf, blocking the UI until initial filtering and event bindings complete.
- It is defined in [`src/options.go`](https://github.com/junegunn/fzf/blob/main/src/options.go) and implemented via deferred UI activation in [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) (lines 1736, 5947, 1874).
- Use it for **multi-stage filtering**, **server-mode stability** (`--listen`), and **deterministic script execution** where side-effects must finish before user interaction.
- Avoid it for simple queries where immediate UI feedback provides better user experience.

## Frequently Asked Questions

### What is the difference between `--sync` and default async mode?

In default async mode, fzf renders the UI immediately and filters results as you type, which provides instant feedback but may show incomplete lists during heavy preprocessing. The `--sync` flag blocks the interface until the initial candidate list is fully processed and any bound startup actions finish, ensuring the user sees a complete, stable list from the first frame.

### Can I use `--sync` with `--listen` server mode?

Yes, and this is a recommended use case. When running fzf as a server with `--listen`, combining it with `--sync` prevents the UI from appearing before the initial data load completes. This eliminates flicker and ensures that any `start` or `load` event bindings execute before the client interface becomes interactive, as demonstrated in [`test/test_server.rb`](https://github.com/junegunn/fzf/blob/main/test/test_server.rb).

### Does `--sync` affect performance?

The `--sync` option does not change the computational complexity of the filtering algorithm itself, but it does delay UI rendering until the initial pass completes. For small datasets, this delay is imperceptible. However, with large input streams or slow preprocessing commands, the interface will remain blank longer, which may feel less responsive compared to the default async mode.

### How do event bindings interact with synchronous search?

When `--sync` is enabled, fzf guarantees that actions bound to the `start`, `load`, `result`, and `focus` events complete before the UI renders. This is implemented in [`src/terminal.go`](https://github.com/junegunn/fzf/blob/main/src/terminal.go) where the terminal activation is deferred until the second tick after these events settle. Even if no items exist to trigger a `focus` event (line 1874), the UI will still activate to honor the synchronous contract.