Buffers, Windows, Tabs: Vim's Three Kinds of Container

This chapter explains the difference between a buffer, a window, and a tab page, and teaches you how to move between them without getting lost.

The Three Concepts

Most editors have one concept: a file you're editing, optionally with tabs across the top. Vim has three:

  • Buffer: a file loaded into memory. You may or may not be looking at it
  • Window: a viewport onto a buffer. You can split a window to see two buffers at once, or the same buffer twice
  • Tab page: a layout of windows. Vim tabs are not a list of open files; they are a collection of windows with their own arrangement

Think of it this way. You have a stack of documents on your desk (buffers). You pick up some of them and lay them out in front of you (windows). If you need a different arrangement for a different task, you flip the desk over to a second surface (tab page) and lay out other documents there.

Most Vim users live in buffers. Windows are common. Tabs are rare.

Buffers

Every file you've opened is a buffer. Switch between them without closing anything:

:e path/to/file     open a file (and create a buffer for it)
:ls or :buffers     list all buffers
:b {num}            switch to buffer number {num}
:b {name}           switch to a buffer by partial name
:bnext  or  :bn     next buffer
:bprevious  or  :bp previous buffer
:bfirst  :blast     first, last
:bdelete  or  :bd   delete a buffer (closes the file)
:bwipeout           stronger: delete the buffer entirely

A typical session, opening three files:

:e src/main.js
:e src/util.js
:e src/index.html
:ls

The output:

  1 #h   "src/main.js"
  2 #h   "src/util.js"
  3 %a   "src/index.html"

Symbols: % is the current buffer, a means active, h means hidden. Numbers are the buffer IDs.

:b 1       switch to main.js
:b util    switch to util.js (partial name works)

Hidden buffers

By default, Vim won't let you leave a buffer with unsaved changes without warning. Set:

:set hidden

Now you can switch away from unsaved buffers and come back. Vim keeps them in memory. This is almost always what you want. Add it to your .vimrc and forget about it.

Buffer-based workflow

Most Vim users work mostly with buffers and very few windows. Rough loop:

  1. Open a handful of files with :e
  2. Navigate between them with :bnext, :bprev, :b partial-name
  3. Close finished ones with :bd

Map a couple of these to leader shortcuts (chapter 9) and buffer switching becomes invisible.

Windows

Windows are visual splits of the current tab page.

:split  or  :sp             horizontal split
:vsplit  or  :vsp           vertical split
:new                        horizontal split with an empty buffer
:vnew                       vertical split with an empty buffer
:sp path/to/file            split and open a file
:vsp path/to/file           vertical split and open a file

Window commands

All window commands start with <C-w>:

<C-w>h    move to the window on the left
<C-w>j    down
<C-w>k    up
<C-w>l    right
<C-w>w    cycle to the next window
<C-w>p    previous window you were in

<C-w>s    split horizontally
<C-w>v    split vertically
<C-w>c    close the current window (same as :close)
<C-w>o    close all other windows (same as :only)
<C-w>q    quit: close window and possibly Vim
<C-w>=    equalise all window sizes
<C-w>_    maximise height
<C-w>|    maximise width
<C-w>H    move the window to the far left
<C-w>J    move to the bottom
<C-w>K    move to the top
<C-w>L    move to the far right
<C-w>r    rotate windows
<C-w>x    swap with next window

That looks like a lot. The ones you'll actually use every day:

<C-w>h  j  k  l     navigate
<C-w>s  v           split
<C-w>c              close
<C-w>=              re-equalise

Five. That is what you need.

Resizing windows

:resize 20                set height to 20 lines
:resize +5                grow by 5 lines
:vertical resize 80       set width to 80 columns
<C-w>+   <C-w>-           grow, shrink height
<C-w>>   <C-w><           grow, shrink width

A count works too: 10<C-w>+ grows 10 lines.

Tab Pages

Tabs in Vim are not a list of open files. They are separate window layouts. Think of them as workspaces. Each tab can have its own set of windows, each showing whatever buffers you want.

:tabnew                   open a new empty tab
:tabnew path/to/file      open a file in a new tab
:tabedit                  same as :tabnew
:tabclose                 close the current tab
:tabonly                  close all other tabs

gt                        next tab
gT                        previous tab
{n}gt                     go to tab number n
:tabs                     list tabs

Tabs are useful when you're working on two unrelated things in the same session, each with its own window arrangement. Most of the time, buffers serve you better.

A rule I try to follow: one tab per distinct task. If I start context-switching, I open a tab. Otherwise, everything is buffers.

A Concrete Example

Say you're editing a Python file and want to see the test file beside it.

:e src/app.py             open the implementation
:vsp tests/test_app.py    vertical split with the test file

Now you have two windows side by side. Jump between them:

<C-w>l    move right (to the test file)
<C-w>h    move left (back to implementation)

Switch the implementation buffer to a helper file while keeping the test visible:

<C-w>h    move to the implementation window
:b helpers   switch that window's buffer

The test window is unchanged. That is the buffer / window split in action.

The Argument List

A fourth, older concept: the argument list, :args. It is the list of files passed on the command line:

vim src/*.py

Inside Vim:

:args               show the argument list
:next  :prev        cycle through arg list
:first  :last
:argadd path        add a file
:argdelete path     remove

In practice, use :args when you open Vim on many files and want to iterate through them, especially with :argdo (run a command on every file in the arglist):

:argdo %s/\vold/new/g | update

That runs a substitute on every file in the arglist and saves them all. Useful for renames.

Diffing Two Buffers

Fast side-by-side diff:

:e a.txt
:vsp b.txt
:windo diffthis

Or from the shell:

vim -d a.txt b.txt

Normal diff commands then apply: ]c next diff, [c previous, do obtain (pull from the other side), dp put (push to the other side).

Common Pitfalls

"I closed a window and Vim quit." <C-w>c closes the last window, which closes the tab, which if it's the last tab, closes Vim. Use <C-w>o if you want only the current window.

"I opened a file and lost my splits." :e replaces the buffer in the current window. To open in a split, use :sp path or :vsp path.

":bd says I have unsaved changes." Save with :w, or force with :bd!. Or set hidden so you can abandon a modified buffer temporarily.

"I can't tell which window is focused." Vim highlights the status line of the active window. If your colorscheme doesn't, set statusline can help (chapter 9). Another tell: the cursor is only in one window at a time.

"I'm drowning in tabs." You're probably using tabs for what should be buffers. Close tabs with :tabonly, open files with :e, use :ls and :b to switch. Treat tabs as rare.

Next Steps

Continue to 08-registers-and-macros.md to learn Vim's 26 clipboards and the macro system that sits on top of them.