Skip to content

Instantly share code, notes, and snippets.

@shmup
Created October 5, 2025 19:56
Show Gist options
  • Save shmup/4148f63324a40f770fd2d6925847ee78 to your computer and use it in GitHub Desktop.
Save shmup/4148f63324a40f770fd2d6925847ee78 to your computer and use it in GitHub Desktop.
comprehensive patterns from junegunn choi's everything.fzf ollection

fzf reference guide

comprehensive patterns from junegunn choi's everything.fzf collection

bind actions

action types

  • execute:CMD - run command in terminal, show output
  • execute-silent:CMD - run command, hide output
  • become:CMD - replace fzf process with command
  • reload:CMD - refresh list with new command output
  • transform:CONDITION - conditional action based on state
  • ignore - do nothing (useful for disabling default enter behavior)

common action patterns

# open selected items
--bind 'enter:execute-silent(for url in {+1}; do open "$url"; done)+clear-selection'

# copy to clipboard and give feedback
--bind 'ctrl-y:execute-silent(echo -n {+2} | pbcopy)+bell+deselect-all'

# reload with different data source
--bind 'ctrl-h:reload(ruby script.rb --list h)+change-multi+change-border-label( History )+top'

# conditional action based on fzf state
--bind 'ctrl-w:transform:
  [[ $FZF_BORDER_LABEL =~ Tabs ]] || exit
  chrome-cli close -t {3} > /dev/null
  echo "reload:ruby script.rb --list t"'

# replace fzf with command output
--bind 'enter:become:gh pr checkout {1}'

# click header word as trigger
--bind 'click-header:transform:
  [[ $FZF_CLICK_HEADER_WORD =~ CTRL ]] && echo "trigger(${FZF_CLICK_HEADER_WORD%:})"'

special variables

selection placeholders

  • {} - current line
  • {+} - all selected items (or current if none selected)
  • {1}, {2}, etc. - specific fields (with --delimiter)
  • {+1}, {+2} - field from all selections
  • {q} - current query string
  • {f} - all selected items as paths (for quickfix)
  • {*f2} - all lines, second field (for aggregation)

fzf environment variables

  • $FZF_PREVIEW_COLUMNS - preview pane width
  • $FZF_BORDER_LABEL - current border label
  • $FZF_CLICK_HEADER_WORD - word clicked in header
  • $FZF_CLICK_HEADER_NTH - column number clicked
  • $FZF_MATCH_COUNT - number of matches
  • $FZF_TOTAL_COUNT - total items
  • $FZF_SELECT_COUNT - number of selected items

event bindings

lifecycle events you can bind to:

--bind 'start:reload:initial-command'        # on fzf startup
--bind 'load:change-list-label:'             # after list loads
--bind 'change:reload:search-command'        # on query change
--bind 'result:bg-transform-footer:...'      # on search results update

practical event patterns

# show loading message then reload
--bind 'start:preview(echo Loading...)+reload:gh pr list'

# exit if no results
--bind 'load:transform:(( FZF_TOTAL_COUNT )) || echo become:echo No items found'

# update footer with aggregated stats
--bind 'result:bg-transform-footer:[[ $FZF_MATCH_COUNT -gt 0 ]] && sort {*f2} | uniq -c'

delimiters and field selection

# use custom delimiter (default is whitespace)
--delimiter "\t"           # tab-separated
--delimiter ":"            # colon-separated
--delimiter "\a"           # bell character (for URLs with colons)
--delimiter " {2,}"        # two or more spaces (regex)

# control which fields to display
--with-nth 2..             # show from field 2 onward
--with-nth ..4             # show up to field 4
--with-nth "{1}\n · {2}"   # custom format with newline

# field for searching
--nth 1                    # search only first field
--nth 2..                  # search from second field onward

preview window

basic preview

--preview 'bat --color=always --highlight-line {2} {1}'
--preview-window 'up,border-bottom,~3,+{2}+3/3'

preview window options

  • position: up, down, left, right
  • size: 60%, 100 (lines)
  • border: border-bottom, border-top, border-left, border-right
  • scroll: +{2} (scroll to line), +{2}+3/3 (offset)
  • wrap: wrap, nowrap
  • follow: follow (auto-scroll to end)
  • hidden: hidden (start hidden, toggle with ctrl-/)
  • ~3 - keep 3 lines at top visible when scrolling

dynamic preview

# use query in preview
--preview 'figlet -f {+} {q}'

# conditional preview based on selection
--preview 'if [[ {3} ]]; then chrome-cli activate -t {3}; fi'

# use environment variable for width
--preview 'gh gist view {1}' GH_FORCE_TTY=$FZF_PREVIEW_COLUMNS

multi-selection

# enable multi-selection
--multi

# disable multi for specific reload
--bind 'ctrl-t:reload(cmd)+change-multi(0)'  # disable
--bind 'ctrl-b:reload(cmd)+change-multi'     # enable

# act on selections
--bind 'enter:execute-silent(for item in {+1}; do echo "$item"; done)'

# handle zero selections in become
--bind 'enter:become:
  if [[ $FZF_SELECT_COUNT -eq 0 ]]; then
    "$EDITOR" {1}
  else
    "$EDITOR" {+1}
  fi'

layout and styling

basic layout

--height 100%              # full screen
--tmux 80%,100%            # tmux popup (width, height)
--reverse                  # top-to-bottom
--layout reverse           # same as --reverse
--border                   # show border
--gap                      # gap between list and preview

labels and headers

--border-label ' TITLE '
--list-label ' Loading... '
--header '╱ CTRL-Y: Copy ╱ CTRL-/: Toggle ╱'
--header-lines 1           # treat first N lines as header
--header-border            # show border under header
--header-first             # header at top (default bottom)

colors and styling

--style full               # enable all styling
--ansi                     # interpret ANSI colors in input
--color 'fg:dim,nth:regular'
--highlight-line           # highlight current line
--wrap                     # wrap long lines
--wrap-sign ''         # indicator for wrapped lines

data input/output

input formats

--read0                    # null-separated input (\0)
--delimiter "\t"           # tab-separated
--tabstop 16              # tab stop width

input modes

--disabled                 # start with search disabled (for reload binding)
--query "initial"          # set initial query
--listen                   # enable HTTP API
--sync                     # synchronous search

output

fzf | while read item; do  # read selections
  echo "Selected: $item"
done

# with IO.popen in Ruby
IO.popen(fzf_cmd, 'r+') do |io|
  io.puts(data)           # write to fzf
  io.close_write
  io.gets                 # read selection
end

advanced patterns

chaining actions

# multiple actions with +
--bind 'ctrl-y:execute-silent(echo {+} | pbcopy)+bell+clear-selection'

# reload after action
--bind 'ctrl-w:execute(delete-item {1})+reload:list-items'

dynamic UI updates

# change border label
--bind 'ctrl-t:change-border-label( New Label )'

# change list label
--bind 'load:change-list-label:'

# jump to top after reload
--bind 'ctrl-r:reload(cmd)+top'

# refresh preview only
--bind 'ctrl-r:refresh-preview'

shell integration

--with-shell 'bash -c'     # use bash for execute/become commands

# multi-line commands
--bind 'enter:become:
  if [[ {3} ]]; then
    command1
  else
    command2
  fi'

sorting and filtering

--tiebreak begin           # prefer matches at beginning
--tiebreak index           # preserve input order
--scheme history           # history-based scoring
--accept-nth 1             # only accept field 1

complete examples

interactive reload with mode switching

fzf --bind 'ctrl-t:reload(list-tabs)+change-multi(0)+change-border-label( Tabs )' \
    --bind 'ctrl-h:reload(list-history)+change-multi+change-border-label( History )' \
    --bind 'ctrl-b:reload(list-bookmarks)+change-multi+change-border-label( Bookmarks )'

search with live reload

RELOAD='reload:rg --column --color=always {q} || :'
fzf --disabled --ansi \
    --bind "start,change:$RELOAD" \
    --bind "enter:become:$EDITOR {1} +{2}"

conditional preview toggle

fzf --preview 'file-preview {1}' \
    --preview-window 'right:60%:wrap' \
    --bind 'ctrl-/:toggle-preview' \
    --bind 'ctrl-w:change-preview-window(80%,border-bottom|hidden|)'

transform with state checking

fzf --bind 'ctrl-w:transform:
  [[ $FZF_BORDER_LABEL =~ Tabs ]] || exit
  close-tab {3}
  echo "reload:list-tabs"'

tips and tricks

  1. use become for final action - cleaner than execute + kill fzf
  2. combine +clear-selection after actions to reset
  3. +bell for feedback on silent actions
  4. transform: for conditional logic without external scripts
  5. --read0 with \x0 for items containing newlines
  6. --disabled + reload bindings for search-as-you-type
  7. --header-first + --reverse for natural top-to-bottom flow
  8. {+f} for vim quickfix when opening multiple files
  9. --tiebreak index to preserve input order
  10. $FZF_PREVIEW_COLUMNS for responsive preview commands
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment