Michał Sapka's website

Buffers, splits and tabs in Vim

Sometimes I work with Vim in brief bursts - open a file, edit it, close it, do something else, open a new instance of Vim with a new file, etc. But on most occasions, I work on bigger things requiring work with multiple files simultaneously. Luckily, Vim has me covered - as a modern editor should.


Whenever a file is opened (be it via ex command or just while opening Vim) or created, a new in-memory representation is stored in memory. Those representations are called “buffers.”

We can list all currently existing buffers for a particular Vim instance via the ex command:


The list will look like this:

1    h  "data/data.txt"    line 23
11  %a "data/other_data.txt"   line 34

We see two opened buffers, each one on a separate line. They have assigned IDs 1 and 11. Next, we have indicators, out which we need to remember that:

  %   | marks buffer opened in the current window.
  #   | denotes an alternative file.
  a   | represents the currently active buffer.
  h   | marks opened buffer, which is now not displayed on the screen.
  +   | marks the modified buffer.
  x   | is telling us that a buffer has read errors.

And lastly, we have the current line in the buffer.

Note that jobs can create dedicated buffers (marked by R for running jobs and F for finished ones)

We can traverse opened buffers using jumps via :b[uffer] ID (for example, :b 1), or with the filename: b other_data, or opening next/previous by using :bn[ext] and :bp[rev]

Any buffer can be closed via :bd[elete].

Since we can have the same buffer opened in different splits, we can see that Vim will keep content in sync, as buffers are not files but in-memory representations.


At any moment, we can divide Vim’s window into multiple split windows, and each split can host any buffer. If we want to divide the current window horizontally, we need to press <C-w>s (or issue ex command :sp[lit]), and if we’re going to split it vertically (which, at least for me, is the default way), we need to press <C-w>h / issue :vsp[lit] . When a new window is created, it will occupy 50% of the parent window. You can close the currently active window by :cl[ose] / <C-w>c, or close all but the active one with :on[ly] / <C-w>o.

Any newly created window will borrow an active buffer from the parent split. When we open a buffer or a file, Vim will open it in the current window.

Moving focus between windows is done with <C-w>h/j/k/l but people commonly remap it to just <C-h/j/k/l>, as this focus is moved all the time.

It’s possible to resize windows in a tiling manner, but I never use it.


Tabs are present in most editors I know. However, Vim treats them differently. For example, in IntelliJ, each opened file is presented as a tab, and each split can have multiple tabs.

In Vim, however, it’s the exact opposite: each tab can hold multiple splits. Ergo, each tab can be a separate workspace with a completely different layout. This makes them much harder to use, as each change of focus can result in a complete change of context. However, this is also their greatest strength - when we want to jump to a different task or project, IntelliJ would like us to create a new window; Vim, on the other hand, allows us to use a dedicated tab. Later, when we return to the previous task, our entire workspace is just as we left it.

We can create a new tab via :tabnew. New tab is added with an empty buffer. When the last window in a tab is closed, the tab is also closed. We can also force close all Windows on the current tab via :tabc.

Changing focus between tabs is done via gt for the next tab, gT for the previous one, and {N}gt will force a given tab to focus.