A Review of Helix after 1.5 Years
2025-03-16
I've been using Helix for roughly 1.5 years now, and for the latest release (25.01) I decided to summarized the thoughts I've been collecting until now.
This article has first been released in the german IT-magazine Golem. Link to article (paywalled)
Helix is a modal terminal editor, like Vim. That means you have multiple modes, one of them dedicated to inserting text into a document (insert-mode) and the others to modifying it in different ways, turning your keyboard into a big game pad specialized in text editing (the default mode of those is called normal-mode). Before I started using Helix, I've been using Vim for a good 7 years, and I want to provide a comparison of these two.
Helix core is much more powerful than Vim, but since there is no plugin system (yet), there is functionality that vim can acquire through plugins, that Helix does not have. The upside is, that you basically don't need to configure Helix. My last Vim config was roughly 100 lines of Vim-script + 500 lines of Lua. My Helix config is 30 lines of toml, and 3/4 of that are keybindings I added to address Vim habits.
A lot of things that require plugins in Vim like autojump, file-pickers, language server support, etc. are built right in and just work. My mind was blown when I opened the editor for the first time in a rust project and was immediately able to use rust-analyzer, without adding a single line of config.
Getting into using a modal editor when you come from something like
VSCode is hard, but it's worth the effort. Helpfully, Helix has
a built-in tutorial. After opening it you can type :tutor
and a
text file is opened that contains an interactive tutorial. You can quit
Helix by typing :q!<Return>
, in normal mode btw. If you're not in normal mode,
you typically get there by pressing Esc
First let me show you a selection of the things Helix can do out of the box, without plugins and without configuration
I think having a file picker is central in an editor. And I don't mean a file tree, but a picker. Let me show you:
This gives you access to all files of the current project. Helix will look for the project root by searching for a .git directory or one of multiple language dependent files that typically are in a projects root, and show all files that are within this root. The picker gives you a fuzzy search, but can also understand regex. Did you type a filename to then realize it exists multiple times within different directories? Just add the directory afterward with a space:
Do you want a list of all toml files in a project? Just use regex and type
toml$
:
There are multiple file pickers. You can open the picker in the project root, in the working directory in which you started Helix, in the directory which contains the file you're currently editing (although the last one requires you to define a custom hotkey), or in the root but only shows those files that are not clean according to git.
You can also move the selection in the file-picker by using <C-n>
(for the
uninitiated: this means Control + n
) and <C-p>
Overall the picker is pretty powerful, but some people just prefer a file tree. This has arrived a couple of days ago in the latest master. Building Helix from source is quite easy, and I've never had a problem using the master branch, but if you are not comfortable doing so, just know that you will have a file tree from the next release on.
Helix primary view on code is defined by two things: The language server protocol and the treesitter. There is a huge list of supported languages and if you use one of those languages, Helix will just work with the standard tooling for that language. If you want to use something that's not supported instead, you can add a treesitter grammar and a language server config, and you're good to go. But most people will not need that. Helix even has support for niche things like Justfiles, cabal config and Elvish.
The fact that Helix just works with about every language (if that language has a good language server) is its biggest strength in my opinion.
I really like this picker. It's like the file picker, but it displays errors. The difference is that the picker here resembles a table:
And the great thing is: you can filter this table by column. Do you want only the errors?
Maybe you want to filter by error code? Also, easy. Just type a percent sign followed by enough letters to unambiguously define the column name, a space and then the value filter.
You can open the diagnostics picker for the current file or the whole project. By the point when I compile a program there are no issues left, because I'll use the project wide diagnostics picker to clean up all errors before I try it.
Want to jump the cursor to another place on the screen? Just type gw
and every word will
have its first two letters overwritten like this:
Then just type the two letters of the word you want to jump to and the cursor will go there.
Another great thing is the tree sitter based code navigation. You get working motions
e.g. mat
- "mark around type", maf
mark around function, ]f
go to next function
and so on. But best feature is the ability to manipulate the current selection by
expanding along the syntax-tree. The following image
shows what happens to the selection if you repeatedly press <A-o>
. The inverse operation
is <A-i>
.
Helix has very powerful selection manipulation capabilities. For example, you can
split a selection on a regex, which is the main way to substitute Vim's substitute
command. You first press %
to select the whole document, you then press s
to split
the selection, and type the word you want to substitute, press enter, and now
all the occurrences of this word are selected. You just press c
now, to change
the selections, and type the new word, to replace all occurrences. Afterward
you go back to normal mode and type ,
to collapse all selections into a single one.
To modify all lines in a certain way, you would first select the whole file,
and then split the selection at \n
.
If you have a bunch of selections you can remove some of them using another regex and so on. The possibilities are endless here, and it's this feature that I would miss the most from Helix if I went back to Vim (which I don't see happening).
You also get a picker for symbols, can perform lsp code actions easily, rename symbols, replace a selection with clipboard content, get a built-in "toggle code comment" command, a huge list of built-in color themes, lsp-snippet support, and have experimental support for interactive debugging. Going into detail for all of those will make this article way too long, so for now I'll just point out that they exist.
While Helix is pretty similar to Vim in many regards, there are differences.
The most significant one is the Kakoune-like input grammar.
In Vim the grammar is <Action><Scope>
.
E.g. diw
for delete in word or yt$
for yank till line end. In Helix, you are basically
always in a pseudo visual mode where every movement action of the cursor will change the
current selection. Your actions then always apply to the current selection. It's as if
you always hit v
in Vim before doing anything else. E.g. in Helix, to do diw
you'd
press ebd
. e
moves the cursor to the word end, b
moves it to the beginning of the
word. Since the cursor was at the end of the word before you now have selected
exactly that word, and d
will delete the current selection. To yank the remainder
of the line, you'd do t<Return>y
. t<Return>
will move the cursor to the end of the line
and select everything between the old and the new position, which will then be
yanked if you press y.
I've often read the opinion that this key map is more effective, but I can't agree with that. Granted, it's a little more intuitive for users new to modal editing. However, in general the key maps are pretty similar, and it didn't take me more than a week to get used to it. I still use Vim navigation in the shell, in my browser, etc. and don't have trouble with using my Vim muscle memory. I guess the trick is to keep using it, so you have both available.
A few things still trip me up though. For example, pressing a
in normal mode at the
end of a line will put the cursor at the start of the next line. You need to use
A
to get a cursor at the end of the current line.
Vim has a scripting language for configuration that can be used to write plugins, and people do so. A small Vim-config still has ~10 plugins, and a lot of people have much more than that. Plugins can be buggy, they can break your config when you update them, you need to configure them, and if you use too many, the startup time will take a noticeable hit.
Helix doesn't have plugins, and the important stuff is in the core. The config language is toml. For me that's an upside, but many people dislike that. There is a plugin system in the making, it will use steel as language of choice. However, since Helix has so much functionality in the core, you will be free to ignore it, if you also dislike the plugin situation in Vim.
I just love Vim's redo command (I mean .
). Helix has it too, but it only
repeats the last action, not the motion before that, which makes it much
less useful. I miss Vim's version.
I did use marks a lot, and Helix doesn't have them. You can set a jump point
with <C-s>
, but that will quickly get lost in the jump-list. This is the
Vim feature I miss the most.
In Vim, you can use <C-o>
and <C-i>
to jump back to where you were
before, and then jump forward again. Helix also has this functionality,
but sometimes, you end up in unexpected places. When this happens,
I'm always more concerned with getting to where I wanted to go than to
reconstruct what exactly just happened, and why <C-o>
or <C-i>
behaved unexpectedly. I tried to construct a situation where
this happens to give an example but didn't succeed.
It doesn't happen often, but often enough, and then it feels like driving and steering left, but your car turns right. It's a small shock and completely breaks the flow.
Other features known from Vim like splits, buffer management, grep search, highlight search, macros, etc. are present in Helix, and typically work as well or better. With tabs being the only notable exception. I never used them though, so I don't miss them.
If you haven't used a modal editor before, and this article inspired you to use Helix, let me give you a few tips:
Esc to go back to normal mode is a choice from a time when the escape key was
in the place where we have capslock on a normal keyboard. Reaching for escape
every time is annoying. Add this to your config to go back to normal mode by
pressing jk
in insert mode.
[keys.insert]
j = { k = "normal_mode"}
gw
is the most useful movement command, and having it available at a single
keystroke is quite useful
[keys.normal]
"ret" = "goto_word"
To have a file-picker in the directory of the current buffer you need to add
a keybinding for it; I chose <Space>o
:
[keys.normal."space"]
o = "file_picker_in_current_buffer_directory"
There is a handy feature called "Cursorline" which highlights the background of the line that the cursor currently is at. To enable it, add this to your config:
[editor]
cursorline = true
Inline hints are awesome, enable them with:
[editor]
lsp.display-inlay-hints = true
The same goes for inline diagnostics:
[editor.inline-diagnostics]
cursor-line = "hint"
The default theme is ... interesting, but "everforest_dark" is great.
theme = "everforest_dark"
You can find Helix's documentation here: https://docs.Helix-editor.com/. It's very useful.
I'm pretty happy with Helix. It's not perfect, but Vim isn't either. You have to
adjust to a slightly different key map, lose marks and .
, and perhaps there's
a feature from a plugin you're using that doesn't exist in Helix. In return, you
get a near-zero-config editor that works with just about every language out of
the box and doesn't require ongoing configuration maintenance.
If you're willing to adjust to it a bit, Helix generally works just as well as Vim, but with much less effort.
Also, Vim on Windows was a nightmare, because many plugins expect standard Unix tools to exist, while Helix didn't cause any problems in a short test.