Vim: You probably do not want to use netrw

Published: | Updated: | 21 min read

Sharing some personal experiences of trying to use Vim's default file browser: netrw

Ask in any vim community what file browser people use, and you will receive very different answers. Modern neovim users will tell you about a plethora of plugins that implement a beautiful file browser akin to what you may find in VSCode or Intellij. Ask old-school vim users, and they might give you similar recommendations or guide you to using :ls, :cd and other UNIX utilities. Only a small minority, however, will claim to utilize netrw, vim’s built-in file browser, and yet few of them will even recommend its usage to others.

It is curious that netrw despite being shipped with every vim version and being currently installed on the machine of every vim user, enjoys so little popularity and usage. You would expect that the typical vim user would immediately jump onto netrw, given it is the most minimal and “bloat-free” option for file browsing in vim. One can even argue that using anything but netrw constitutes some form of redundancy: Why would you use a file manager plugin if netrw is right there? This is not news as talks on what vim can already do without plugins will quickly point out. Similarly, when the typical “side-drawer file viewer” became popular in other IDEs, many people flocked to plugins like NERDtree to get a similar experience in vim, and this inspired all the more posts on why one should be using netrw instead.

It is, however, also not a big secret that netrw enjoys a rather bad reputation. You will find many people on Reddit or other forums describe it as buggy and non-extensible. There is even an entire project vim-vinegar with the expressed purpose of trying to fix the many issue of netrw. However, that attempt didn’t last long when eventually netrw’s issues proved too difficult to consistently fix. Consequently, people abandoned the idea of ever fixing netrw and moved onto file manager plugins with the same “netrw experience"" like vim-dirvish, ultimately leading back the aforementioned “problem of redundancy”.

But surely netrw can’t be that bad!

Going against all the disfavor netrw is receiving, I wanted to give it a proper try. Perhaps all the posts encouraging its usage were right. Perhaps it actually is the most clean and minimal way of browsing files! At least these were the thoughts I had prior to actually trying out netrw. Once I had actually used it for more than a few hours, I had developed a rather clear opinion: you probably do not want to use netrw as your vim file browser. In fact, I believe that netrw should even be removed from vim (or at least neovim) given the barely maintained state it is in.

So what are the problems of netrw that led me to such a harsh opinion? What is behind all the bad reputation netrw has online? To answer that, let us go on the same journey of using netrw as I did, and perhaps that experience will speak for itself.

Starting off: Setting up netrw

We start our journey by setting up a modern netrw config based on some popular recommendations. The goal of the setup should be a netrw config that can reasonably compete with modern file browser plugins. That is, we are trying to make netrw behave like the typical “side-drawer browser” and to have a feeling akin to what one is used to from other IDEs. Of course, some “netrw purist” might claim that this is not how netrw ought to be used / configured, yet the point of this very setup is to actually test the claims that netrw can be used as a modern replacement. I will concede that maybe some issues of netrw are avoided if one uses it in this pure way, yet then one cannot claim that netrw is a valid replacement for other plugins. As much as I respect people who comfortably use old-school workflows, this setup is about netrw holding up to the more “modern” workflow standards.

Setting up netrw is very easy. Simply ensure no other plugins unloads it and then utilize existing vim options to configure it:

vim.g.netrw_banner = 0 -- Hide the giant (useless) banner atop netrw
vim.g.netrw_altv = 1 -- Create the split of the netrw window to the left
vim.g.netrw_browse_split = 4 -- Open files in previous window. This emulates the typical "drawer" behavior
vim.g.netrw_liststyle = 3 -- Set the styling of the file list to be that of a tree
vim.g.netrw_winsize = 14 -- Set the width of the "drawer"

This config produces a nice sidebar-drawer with a file tree, all realized in netrw.
One thing missing, however, is icons. The default netrw look just utilizes a few ASCII symbols and colors, which aren’t really an eyecandy. Luckily the neovim plugin netrw.nvim fixes just that by adding some fancy devicons to netrw. All this then yields us a very promising look for our netrw setup:

Picture of an opened netrw file browser which looks indistinguishable from a typical modern file browser plugin

Quite fancy and so little setup necessary! It almost looks indistinguishable from a modern file browser plugin too. But let us not judge a book by its cover, and actually start diving into using netrw.

The bare minimum: Browsing files

One thing I conveniently skipped when showing the screenshot from before is how to actually open the netrw drawer like that. A quick glimpse at the documentation will reveal two ways of doing so: :Lexplore and :Vexplore. The former option is actually a more recent feature added into netrw aiming to emulate exactly the behavior from NERDtree which is to toggle the drawer on and off. The latter option :Vexplore will open the drawer, but not close it, meaning it is no use when trying to emulate a “modern file browser experience”.
Of course with :Lexplore right there available, why would we care about any other option? Truthfully, we don’t, unless of course it just so happens that the usage of :Lexplore introduces many new issues down the line, but let us dismiss that possibility for now (foreshadowing)…

So far everything looks good. We bind :Lexplore to our favorite keys and can get started exploring our local file tree. All seems well and good, but how about we try opening some file from the drawer? As we would expect, the file is opened in a new buffer to the right of the drawer and our cursor immediately placed into it. While some details maybe a bit clunky compared to modern plugins, it is nothing one cannot get used to. Thus far netrw seems more than capable!

Yet as we start opening files and toggling on and off the drawer, a glaring issue starts to arise. Looking into our list of open buffers (:ls) or simply using a plugin that shows them (e.g. bufferline.nvim) will quickly reveal that with every usage of netrw, we gradually accumulate more and more empty buffers in our buffer list! That is right: Every time we open a file and toggle on the drawer, netrw will create a new unnamed buffer to pollute our buffer list with. In fact, this issue has been well-known for years, yet at the time of writing this post, no official patch is implemented. And so we face our first major issue with netrw that we have to fix ourselves. After all, a file manager that pollutes our buffer space is practically unusable. Imagine using a modern IDE and it creates an empty tab every time you open a file.

Most people might already give up on netrw at this point, but not us. Luckily, a kind user in the GitHub issue above has provided us with a nice “fix” to our little noname-buffer problem: Simply delete all unmodified, unnamed buffers whenever we toggle the drawer. Admittedly this is the cheapest fix imaginable, but sadly also the only one available to us apart from fixing netrw’s source code ourselves. Transcribing the kind user’s script into Lua yields us this beauty:

-- Remove all empty "No Name" buffers that are unmodified
local function clean_empty_bufs()
	for _, buf in pairs(vim.api.nvim_list_bufs()) do
		if
			vim.api.nvim_buf_get_name(buf) == ""
			and not vim.api.nvim_buf_get_option(buf, "modified")
			and vim.api.nvim_buf_is_loaded(buf)
		then
			vim.api.nvim_buf_delete(buf, {})
		end
	end
end

-- Clean up netrw's empty buffer artifacts and let that logic toggle it
local function toggle_netrw()
	clean_empty_bufs()
	local flag = false
	for _, buf in pairs(vim.api.nvim_list_bufs()) do
		local e, v = pcall(function()
			return vim.api.nvim_buf_get_var(buf, "current_syntax")
		end)
		if
			(e and v == "netrwlist")
			and not vim.api.nvim_buf_get_option(buf, "modified")
			and vim.api.nvim_buf_is_loaded(buf)
		then
			flag = true
			vim.api.nvim_buf_delete(buf, {})
		end
	end

	if not flag then
		vim.cmd(":Lexplore")
	end
end

Every time we would call :Lexplore we now call our function toggle_netrw() instead which will clean up the empty buffers for us. Occasionally, an empty buffer will still be opened here and there, but just toggling netrw again will get rid of it.
The attentive reader may have picked up from the GitHub issue that this bug is a consequence of using :Lexplore, meaning we could have maybe avoided it if we just used netrw differently. However, it already speaks volume about the quality and “competitiveness” of netrw when we can’t even use a basic feature of it.

With our fix implemented, it seems like we can properly browse our files without too many issues. However, as we start getting adjusted to the way netrw works, we will quickly find out that this was just the tip of iceberg.

Previously, netrw behaved fine when we directly opened a directory in vim like so: vim dir/, but what happens if we open a file? What directory will netrw take as its root then? Well, as it turns out it will always take your shell’s current directory which is anything but desirable! Suppose I want to open a file in my /tmp/ directory after I’ve just spawned a new shell which by default starts in ~/. I would type vim /tmp/myfile.txt and sure enough I open my file. However, if I toggle netrw in that opened file, it will now show me my home directory ~/ and not /tmp/ which is what every other file browser would have done. Essentially, netrw behaves very counter-intuitively when picking its root directory.

Of course, this behavior is not a bug, but rather a consequence of netrw’s implementation being more tailored toward the old-school way of heavily interfacing with the user’s shell. Still, for our purposes of a modern file browser, it is an issue we have to fix.
Unfortunately, none of netrw’s settings can help us in changing this behavior, as it will determine its root based on vim’s :pwd. We might be tempted to use something like vim.opt.autochdir, but this will cause netrw to always use every file’s immediate parent-directory as a root, which is not great when trying to have something like “project directories” in which we always want one particularly directory to remain the root. Besides, autochdir tends to break many vim plugins, so instead of autochdir-ing, we will just implement a small hack that manually sets the pwd:

vim.cmd(":cd %:h")

Simply execute this command sometime after vim startup and netrw should have a more intuitive root directory selection.

Unfortunately, we still are not done with the browsing issues of netrw. As we go about browsing our files, we may at some point wish to unfocus the window vim is running in. This also is something our netrw configuration won’t take lightly.
If you expand any folder with sub-folders in netrw, unfocus vim and then refocus it, netrw will somehow “unload” these sub-folders, displaying them as empty files! Trying to open these “sub-folder files” won’t do anything but spawn another window of netrw without actually expanding the sub-folder in question. Of course, this is also a known open issue and once again we must implement the fix ourselves by changing how netrw browses our files:

vim.g.netrw_fastbrowse = 2

Setting this option prevents the issue when refocusing vim, however, certain other actions like file operations may still cause sub-folders to sometimes vanish. Perhaps no one is perfect.

We haven’t even moved past trying to use the most basic feature of a file browser: browsing files, and already had to fix three major issues ourselves! Truly an amazing experience, is it not? But what if I told you that we are still not done? What if I told you that netrw will also mess with your clipboard registers when it is opened? What if I told you that netrw error messages are always opened in a new buffer-split that you must manually close? What if I told you that some netrw keys cannot be remapped? What if I told you that netrw spawns new buffers in a weird way when opening a directory directly? What if…? The list just goes on and on, but maybe it’s time to stop here.

For all intents and purposes, after spending just enough time on netrw, you can reasonably fix most of its issues and get a somewhat reasonable file browsing experience, however, you will find that all that effort will hardly get you the same comfort you may be used to from modern alternatives… And we haven’t even talked about things beyond just opening and closing files.

Demandable basics: Creating files

After overcoming all the small hiccups of browsing files, we can now comfortably use netrw to open, close and browse any file/directory we want. Perhaps with some minor impediments here and there, but the experience seems solid enough. Let us now try to create some new files and directories, which would be a simple feature you would expect from any file browser.

To create a directory we simply press d and netrw will ask us for the name our new directory. After typing a name and confirming, the directory will then be created as a subdirectory in the directory we have opened in netrw. Great, all works as expected.

Let us now create a file by pressing % and netrw will again ask us for the name of our new file. We confirm and at first all seems correct. The file is created where it should be, albeit with some minor issues of the netrw not refreshing to show the creation of that file. However, if we actually take some time to experiment around, we will eventually find that netrw has not created the file where want it to be! That is because netrw only creates files in the current working directory, disregarding your position inside the netrw window. Of course, we can always change our current working directory via :cd, yet this is hardly a solution given we have a file browser that should manage this functionality. We might be tempted to utilize settings such as vim.g.netrw_keepdir and vim.opt.autochdir again which you would think help this issue, but actually don’t make any difference. In fact, even I cannot explain how exactly netrw decides where to create new files as sometimes not even the usage of :cd helps in informing that choice.

And so we have reached yet again another major roadblock in using netrw. What good is a file manager that cannot even consistently create new files? At this point almost everyone would give up further trying to make netrw work, but not us! Going on an extensive troubleshooting quest, we eventually end up at a Reddit post giving us both a reason for and a solution to the issue we are facing:

The implementation of :Lexplore seems to once again mess up something and causes certain operations in netrw to not be correctly performed, thus creating new files not where we want them to be.
As u/LinearG kindly explains, the “fix” to our issue is to first press C “to change the current netrw window to the editing window” and to then press cd to make netrw enter the actual directory we want to be in. Why we must do this not even they know, but sure enough it does the trick… at first. While utilizing this solution does create files correctly, we now have the issue that the new file will be created inside the netrw window. Truly delightful, no? Fixing one issue creates a new one and once again the improper implementation of :Lexplore is probably to blame. At this point truly everyone, no exception, would give up on netrw and sure enough, the OP of the Reddit post did exactly that when faced with this issue. But once again: not us!

Since we have to create a keybinding anyway that incorporates the Ccd execution before any file creation, we might as well expand the command of that keybinding to fix our new issue too. Eventually we arrive at this monstrosity of a hack:

vim.keymap.set("n", "a", "Ccd%:w<CR>:bw<CR>:Lexplore<CR>", { remap = true, buffer = true })

Since remapping to % will end up in a recursive, endless loop, we have to change our mapping for creating new files to something else (a in this case). Not that this matters, given % is a rather obscure key for the relatively often used operation of creating files.
Let us now detail what this command does:

  • Ccd%: This is taken straight from the Reddit post and creates files as expected, but now leaves us with the issue of new files being opened inside the netrw window.
  • :w<CR>:bw<CR>: This writes the newly created file in the netrw window right away. We then delete the netrw window via :bw. This means that upon creating a file, netrw is simply closed.
  • :Lexplore<CR>: Since closing netrw upon file creation is a bit clunky, we reopen it manually again.

And so we have successfully fixed file creation in netrw! It only took us quite a bit of troubleshooting and working out some insane hack to get the most basic functionality you would expect a file browser to offer.

Once again we notice that :Lexplore is a major factor in introducing these bugs. The foreshadowing was not without reason, and it makes me truly wonder how much testing (if at all) the implementation of :Lexplore has received.

Pushing the limit: More file operations

At this point we could already conclude everything about netrw. Given the extensive hassle to even have it work for browsing files and now having had to mess long to get file creation working, it wouldn’t seem like any additional features of netrw are very promising. However, why don’t we still push onward and move onto the final feature a file browser should have: performing file operations. Clearly renaming, moving and copying files is something every file browser should readily offer and maybe this is where netrw can shine after all.

Unfortunately for us, however, the most important file operations of copying and moving work anything but great in netrw. Just like in any other terminal file browser, you first mark the files you want to copy/move and then perform the operation by pressing another key inside your target directory. So far so good. However, in netrw we don’t enter the target directory, instead we simply mark it by pressing mt while hovering over it. This actually is a rather great way to go about marking a target directory, yet compared to other file browsers it is also quite unintuitive. The real issues, however, start already when using mt because doing so will inexplicably create another netrw split out of thin air. That’s right, every time we want to mark a directory with mt, netrw will create a new buffer + split which sometimes cannot even be closed without pressing ZZ / ZQ! When faced with this issue, I had first tried to find another hacky solution by calling :bw right after mt, yet this was only able to partly fix the issue as sometimes the new split could not be deleted like that. Being hardly satisfied, I moved on regardless to test whether the file operations themselves would even work at all, and only faced more issues.

I noticed that marking my files and afterward the directory would consistently give me errors:

**error** (netrw) tried using g:netrw_localcopycmd<cp>; it doesn't work!

the same happened for moving files as well. Only when I reversed the order in which I marked target files and target directory (so destination first, source second) did the operation succeed for copying, but not moving. This is funny because it led me to initially believe that netrw would force you to always specify your source and destination that way, simply because the actual feature was entirely broken by a major issue. Of course, you guessed it, that issue is known and even has an open merge request! Yet for me not even the “fix” of setting g:netrw_keepdir=0 mentioned in the issues helped, as this only raised another error message:

**error** (netrw) move failed; perhaps due to vim's current directory</tmp/test/c> not matching netrw's (/tmp/test/c/) (see :help netrw-cd)

Needless to say, :help netrw-cd could not explain why it struggled with the lack of a trailing slash and even utilizing netrw’s cd did not help the issue. Ultimately, I gave up on ever using netrw’s broken file operations and decided to hack together my own.

But we aren’t out of the woods yet. Despite file operations already not really working, they would have not worked anyway had we tried to apply them to directories. That is because marking directories is broken in that it will inexplicably append the final subdirectory twice to its path:

/tmp/folder/a/ -> /tmp/folder/a/a/

why that happens, I once again do not know. But it did mean I also had to fix this wrong path as part of hacking netrw’s buggy marking implementation.

File deletion and renaming also only works partially well. Because of the aforementioned issue with marking directories, performing operations consistently on them becomes an issue already. But even when trying to delete or rename regular files, netrw will sometimes begin reading some strange non-existent paths, ultimately resulting in the operation failing. To be fair, though, renaming and deleting works a lot better than copying or moving does. More importantly, netrw will also ask for confirmation for these operations. Who knows what damage could have been done had it simply deleted some imaginary file path!

Conclusion

Many hours, self-made fixes and hacks later, we eventually find netrw to be a barely acceptable file browser that is still lacking in many ways. It comes really to no surprise then that netrw experiences such a bad reputation and that only few people are using it. Despite all that, I do not think that the people who “recommend” netrw do so in bad faith. Even with all hacks applied, it remains the most minimal solution, yet finding a justification to actually use it beyond that is very difficult. At the end of the day you will save yourself much more time and nerves not using netrw than when you are trying to make it work the way you want.

It also doesn’t help that netrw is barely extensible in that regard. If netrw could have been easily (and successfully) fixed, surely more people would use it and projects vim-vinegar would have been considerably more popular. Yet netrw is not extensible at all. Even the aforementioned netrw.nvim plugin only realizes its features by forcing itself into netrw’s buffers and manipulating those. The idea of LSP or gitsign support in netrw seems therefore very distant.

I do hope that the maintainers of netrw eventually fix the many issues of it and maybe even turn netrw into something that can compete with modern file browsers. Given its age and lack of maintenance, however, that seems rather improbable to ever happen. Instead, I rather advocate netrw is simply removed from neovim and also not replaced with any alternative. I believe a minimal editor like vim should have its users bring their own solution to file browsing with them, thus permanently resolving the issue of potential “redundancy”. Of course, whether neovim can actually implement such a change is another question, but is definitely more likely than vim ever doing so, since netrw does happen to be a dependency for certain features like downloading spellfiles.

With all that said and shown, it can be concluded that the community is more than justified with its negative attitude toward netrw. At the end of the day, you probably do not want to use netrw as your file browser, and it is as easy as deactivating the netrw plugin and using just about anything else.