What Is Find and Replace in Helix?
Helix is a modal text editor inspired by Kakoune and Neovim, built from the ground up with modern features like tree-sitter syntax highlighting, multiple cursors, and a powerful built-in find and replace system. Unlike many editors that rely on external plugins for advanced search operations, Helix ships with an integrated, keyboard-driven find and replace workflow that works seamlessly across single files, multiple selections, and entire projects.
The find and replace mechanism in Helix operates through a combination of buffer-specific search commands and a dedicated :replace command that respects selections. The core philosophy is select first, then act — you identify what you want to change, select it using Helix's powerful selection primitives, and then apply transformations only to those selections. This approach eliminates the need for complex regex lookaheads or lookbehinds in many cases, because you can visually or structurally narrow down targets before executing a replacement.
Why Find and Replace Matters in Helix
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Efficient text manipulation is the heart of any editor, and Helix's design choices make find and replace particularly important for several reasons:
- Multiple cursors are native — Helix treats multiple selections as first-class citizens. When you perform a replace operation, it applies simultaneously across all active selections, giving you instant visual feedback.
- Structural editing via tree-sitter — Helix understands your code's syntax tree. You can select nodes like function bodies, class definitions, or entire blocks, then replace only within those structural boundaries.
- No regex over-reliance — Because you can narrow selections first, you often don't need complex regular expressions. Simple string replacements become powerful when combined with precise selections.
- Project-wide refactoring — Helix supports multi-file search via
:rsearch(or a workspace-wide grep) and allows you to batch replacements across all matches.
Getting Started: Basic Search
Before you can replace text, you need to find it. Helix uses a Kakoune-inspired search model. From normal mode, press / to search forward or ? to search backward. The search is incremental — as you type, the cursor jumps to the first match.
# In normal mode, type:
/function
# This highlights all occurrences of "function" in the buffer
# Press Enter to commit the search, or Esc to cancel
# Use n / N to jump to next / previous match
The search pattern supports full regex syntax. For example, to find all words starting with "get" followed by a capital letter:
/\bget[A-Z]\w+
Once you have a match highlighted, you can extend your selection using Helix's selection manipulation commands.
Single-Selection Replace: The :replace Command
The primary replacement mechanism in Helix is the :replace command (aliased as :re). It operates on the current selection(s). The syntax is straightforward:
:replace <pattern> <replacement>
# Aliases:
:re <pattern> <replacement>
# Examples:
:re true false # Replace "true" with "false" in current selection
:re \b\d+\b "NUMBER" # Replace standalone numbers with "NUMBER"
By default, :replace replaces all occurrences of the pattern within each selection. If you want to replace only the first occurrence in each selection, add the /c flag (confirm). This flag also enables interactive confirmation for each match.
Replacement Flags
Helix provides several flags to control replacement behavior:
/cor/confirm— Confirm each replacement interactively. You can pressyto accept,nto reject,ato accept all remaining, orqto quit./gor/global— Explicitly replace all occurrences (this is the default, but can be combined with/c)./ior/ignore-case— Case-insensitive matching./xor/exact— Treat the pattern as a literal string, not a regex.
# Replace all "foo" with "bar", case-insensitive, with confirmation
:re /i /c foo bar
# Replace literal "a[0]" (not regex) with "array[0]"
:re /x a[0] array[0]
Working with Multiple Selections
Helix truly shines when you combine find and replace with multiple selections. Here's a common workflow:
- Search for a pattern using
/ - Select all matches with
%(select_all) or manually add selections withn - Run
:replaceto transform all selections at once
# Step-by-step example:
# 1. Search for all "const" declarations
/const\s+\w+
# 2. Select all matches in the buffer
%
# 3. Replace "const" with "let" within those selections
:re const let
# Now all "const" declarations become "let" declarations
You can also narrow selections before replacing. For instance, after selecting all matches with %, use Alt-s to split selections by a delimiter, or use Alt-_ to remove selections that don't contain a pattern.
# Select all lines containing "TODO"
/TODO
%
# Now replace "TODO" with "FIXME" only on those lines
:re TODO FIXME
# Or split selections further:
# Use Alt-s to split by spaces, then replace specific words
Replacing Within Structural Boundaries (Tree-sitter)
One of Helix's most powerful features is its ability to select syntactic nodes using tree-sitter. This allows you to confine replacements to specific code structures without writing complex boundary regexes.
# Inside a function body:
# 1. Move cursor inside a function
# 2. Select the entire function body with Alt-f (select_function)
# 3. Replace all "var" with "let" ONLY within that function body
:re var let
# Replace within a class definition:
# 1. Move cursor to a class
# 2. Alt-c selects the class
# 3. Replace "self." with "this." only inside that class
:re self\. this.
Common tree-sitter selection keys:
Alt-f— Select function bodyAlt-c— Select classAlt-a— Select argument listAlt-o— Select commentAlt-p— Select paragraph
These structural selections compound beautifully with replace operations. You can select multiple functions (using % after a search or by manually adding selections with Alt-n) and replace across all of them simultaneously.
Project-Wide Find and Replace
Helix supports workspace-wide search and replace through its picker and grep integration. The workflow differs slightly from buffer-local operations.
Using the Workspace Picker
Press Space / (or Space f for file picker) to open the workspace picker in fuzzy search mode. To perform a global search, use:
# In normal mode:
Space / # Open workspace search picker
# Type your pattern and press Enter
# All matches across the workspace appear in the picker
# Use Ctrl-c to copy the results, or select files to open
To replace across all matching files, you can use the :rsearch command combined with :replace in a macro-like workflow, or pipe the results through Helix's command system.
Using :rsearch and Quickfix
# Search for a pattern across the workspace
:rsearch oldFunctionName
# This populates the quickfix list with all occurrences
# Navigate through matches with Space ] and Space [
# For each file, open it, select matches with %, and run:
:re oldFunctionName newFunctionName
# Then save and move to the next file
Using External Grep with Helix
For more complex project-wide replacements, you can use external tools and leverage Helix's ability to open files at specific locations:
# From your terminal (outside Helix):
grep -rn "oldImport" src/ | helix --line-number
# Or within Helix's integrated terminal:
# Use Ctrl-z to suspend, run sed replacements, then resume
# Or use :sh to run a shell command while Helix stays open
:sh sed -i 's/oldImport/newImport/g' src/**/*.rs
Regex Capture Groups in Replacements
Helix supports regex capture groups in the replacement string using $1, $2, etc., or named groups with ${name}. This is essential for rearranging text.
# Swap two words separated by a comma
:re (\w+),\s*(\w+) $2, $1
# Example: "apple, banana" becomes "banana, apple"
# Convert snake_case to camelCase within selections
:re _([a-z]) \U$1
# Example: "user_name" becomes "userName" (with uppercase N)
# Add quotes around numbers
:re \b(\d+)\b "$1"
# Rearrange function parameters
:re func\((\w+),\s*(\w+)\) func($2, $1)
Helix uses Rust's regex engine, which supports:
$N— Capture group by index (1-based)${name}— Named capture groups$0— The entire matched string$$— A literal dollar sign
Case Transformation in Replacements
Helix supports case modifiers within replacement strings, prefixed with \:
# \u - uppercase next character
# \U - uppercase until \E or end
# \l - lowercase next character
# \L - lowercase until \E or end
# \E - end case transformation
# Capitalize first letter of each word
:re \b(\w)(\w*)\b \u$1\L$2
# Example: "hello world" becomes "Hello World"
# Force entire match to uppercase
:re \w+ \U$0
# Force entire match to lowercase
:re \w+ \L$0
Interactive Confirmation Mode
When you need fine-grained control over each replacement, use the /c flag. Helix enters a confirmation loop where you can inspect each match before deciding.
# Replace "old" with "new", confirming each
:re /c old new
# During confirmation:
# y - yes, replace this match and move to next
# n - no, skip this match and move to next
# a - yes to all remaining (stop confirming)
# q - quit, stop all remaining replacements
# Esc - same as quit
This mode is invaluable for refactoring where context matters — you might want to replace a variable name only in certain scopes but not others, and the structural selection approach isn't granular enough.
Replacing Across Multiple Files with Macros
Helix supports recorded macros (using Q to record and q to replay). You can combine this with find and replace for repetitive multi-file operations.
# Record a macro that:
# 1. Opens a file from the picker
# 2. Performs a search
# 3. Selects all matches
# 4. Runs a replace
# 5. Saves and closes
# In normal mode:
Q w # Start recording to register w
Space f # Open file picker
# (select file manually, press Enter)
/old_pattern # Search
% # Select all matches
:re old_pattern new_val # Replace
:x # Save and close
q # Stop recording
# Now replay with:
Space f # Open picker
# (select next file)
q w # Replay macro on that file
Replacing with the Pipe Command
Helix can pipe selections through external commands and capture the output back into the buffer. This is effectively a "replace with shell command output" feature.
# Select a JSON blob, pipe through jq to format it
# 1. Select the text (e.g., with Alt-p for paragraph)
# 2. Press | (pipe)
# 3. Type: jq .
# The selection is replaced with the formatted output
# Sort lines within a selection
# 1. Select multiple lines
# 2. | sort -r
# Selection is replaced with reversed sorted lines
# Base64 encode a selection
# 1. Select text
# 2. | base64
This pipe-based replacement is distinct from :replace but serves a similar transformative purpose. It's especially useful for formatting, encoding, or processing text with external tools.
Common Workflows and Patterns
Renaming a Variable Across a File
# 1. Move cursor to the variable
# 2. Use * (or Alt-o) to select the word under cursor
# 3. % to select all occurrences in the buffer
# 4. :re old_name new_name
# Done — all occurrences are renamed
Adding a Prefix to Multiple Lines
# 1. Select multiple lines (use Shift-J to extend downward)
# 2. Run replace with ^ anchor
:re ^ "// "
# This adds "// " to the beginning of each selected line
# Equivalent to commenting out those lines
Removing Trailing Whitespace
# Select the entire buffer with %
# Run:
:re \s+$ ""
# Or for all lines:
:re \s+$ "" /g
Converting Tabs to Spaces Within Selections
# Select the region
# Replace tabs with 4 spaces
:re \t " "
Wrapping Text in Tags (HTML/XML)
# Select the text to wrap
:re (.+) \n$1\n
# Note: \n inserts a newline in the replacement
# This wraps the selection in tags
Best Practices
- Select first, replace second. The most efficient Helix workflows involve narrowing down selections before running
:replace. Use structural selections (Alt-f, Alt-c), search (/), and selection combinators (%, Alt-n, Alt-_) to precisely target what you want to change. A poorly scoped replacement can cause collateral damage.
- Use
/c confirmation for critical changes. When replacing in important code or when the pattern might have false positives, always add the /c flag. The few seconds you spend confirming each match prevent minutes of debugging.
- Prefer structural selections over complex regex. Instead of writing a regex that matches "function calls with three arguments inside a class method", select the method body with
Alt-f, then run a simpler replace pattern. The selection context does the heavy lifting.
- Leverage multiple cursors visually. Helix shows all selections highlighted. Before committing a replace, glance at the highlights to verify they cover exactly what you intend. If selections extend too far, use
Alt-; (trim selections) or Alt-_ (filter selections).
- Use
/x for literal strings. When replacing text containing special regex characters (dots, brackets, asterisks), use the /x flag to avoid escaping hell. It's cleaner and less error-prone.
- Test regex patterns incrementally. Before a full replace, test your pattern with a plain search (
/). If the search highlights match your expectations, the replace will too. If not, refine the pattern.
- Combine pipe and replace for complex transformations. If you need to transform selections in ways regex can't easily express (sorting, encoding, JSON formatting), pipe through external commands. Then use replace for any remaining cleanup.
- Save macros for repetitive multi-file replacements. If you find yourself doing the same find-and-replace sequence across many files, record it as a macro. This turns a 5-step process into a single keystroke.
Edge Cases and Troubleshooting
Regex Escaping
Remember that Helix uses Rust's regex flavor. Characters like ., *, +, ?, [, ], (, ), {, }, ^, $, |, and \ have special meanings. Escape them with \ when you need literal matches, or use the /x flag.
# Replace literal parentheses (without /x):
:re \(.*\) "removed"
# With /x flag — cleaner:
:re /x (.*) "removed"
Newlines in Patterns and Replacements
To match across lines, use \n in your pattern. In replacements, \n inserts a newline. Note that by default, . does not match newlines — use (?s) for single-line mode or [\s\S] as a workaround.
# Replace two consecutive lines joined with a comma
:re (\w+),\n(\w+) $1 and $2
Zero-Length Matches
Helix handles zero-length matches (like \b or lookahead assertions) gracefully. However, replacing a zero-length match inserts text at that position without removing anything.
# Insert a comma at word boundaries (not removing anything)
:re \b ","
# This inserts commas between words — use carefully
Conclusion
Helix's find and replace system is a carefully integrated part of its selection-first editing model. By combining precise structural selections, incremental search, regex capture groups, interactive confirmation, and multi-file workflows, you can handle everything from quick local edits to complex project-wide refactorings without leaving the editor or reaching for external tools. The key insight is that Helix encourages you to see what you're changing before you change it — selections provide visual feedback, confirmation mode adds explicit approval, and structural boundaries keep replacements contained. Mastering find and replace in Helix means mastering the editor's selection primitives, and once you internalize that workflow, text manipulation becomes faster, safer, and more intuitive than in traditional editors that separate search from selection from action.
🚀 Need a reliable AI agent for your project?
Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.
Get Started — $23.99/mo