Projects
β€ΊNeovim Plugins
β€Ίnvim-toggleterm
Active

~/plugins/nvim-toggleterm

A dependency-free Neovim plugin for a floating terminal with multi-session support - centered on screen, winbar tab bar, persistent buffers, auto-resizing on UI changes.
LuaLua
NeovimNeovim 0.8+
πŸ“…March 19, 2026
πŸ“„~297 lines
Tech Stack
neovimNeovim
luaLua
vim.api floating windows
vim.fn.termopen()
Source Code

01The Problem: Leaving the Editor for a Terminal

Every Neovim user has a workflow that involves a terminal. Running tests, checking git logs, starting dev servers - these things happen constantly. The question is how you get to a terminal without breaking your concentration.

The common approaches all have trade-offs. A separate terminal window means alt-tabbing - a context switch that adds up. A built-in :terminal split takes up permanent screen real estate. Tmux panes help, but require a context switch of a different kind.

What I wanted was simpler: press a key, a floating terminal appears centered on my screen. Press it again, it disappears. My code is still exactly where I left it. The terminal session persists - same shell, same directory, same history - I just can't see it until I need it. And when I need a second session (say, one for tests and one for a dev server), I want to spin it up with a single keymap and switch between them without leaving the editor.


02Why Write My Own Plugin?

There are toggle-terminal plugins out there - toggleterm.nvim being the most popular. But popular plugins come with surface area: multiple terminal types, split modes, shade options, send-to-terminal integrations. All useful features, but not the ones I needed.

After building nvim-pomodoro (which also uses a floating popup), I had a solid foundation in Neovim's floating window API. This plugin pushed that knowledge further: handling terminal buffers specifically, managing insert mode transitions, dealing with the edge cases that come with jobstart and job exit callbacks, and now maintaining a session list with a winbar tab bar for navigation.

The result is a plugin small enough to read in one sitting - one file, ~300 lines - and exactly as capable as I need it to be.


03Design Principles

01
Buffer Reuse
Each session gets its own buffer (bufhidden=hide). Close the window and the buffer lives on - reopen it and the same shell, same directory, same history is exactly where you left it. Switching between sessions swaps the buffer via nvim_win_set_buf with no window teardown.
02
Auto-Resize
The floating window geometry is computed as a fraction of vim.o.columns / vim.o.lines. A VimResized autocmd fires on editor resize and recalculates position and size automatically.
03
Single File
The entire plugin is ~300 lines in one file: init.lua. Read it to understand it. Modify it to own it. No layers of abstraction between you and the behaviour.
nvim-toggleterm/
└── lua/nvim-toggleterm/
└── init.lua -- Everything: setup(), open/close/toggle(), new/close/rename/switch_terminal()

04Features

  • Floating terminal window centered on screen - no split, no tab
  • Multiple named sessions - each with its own persistent buffer and independent shell
  • Winbar tab bar shows all active sessions; active session is visually highlighted
  • Create, rename, close, and navigate sessions via commands or buffer-local keymaps
  • Configurable size (width / height as fractions of editor dimensions)
  • Configurable border style: single, double, rounded, solid, shadow, or custom
  • Optional floating window title (Neovim 0.9+)
  • Auto-enters insert mode on open (start_in_insert)
  • Auto-resizes on VimResized - always stays proportional
  • Optional user commands (:FloatingTerminal*)
  • Optional keymap generation - one config line to bind the toggle key
  • Auto-close on shell job exit (configurable)
  • Zero dependencies - pure Lua, ships with Neovim 0.8+

05Commands & Config

User Commands

CommandDescription
:FloatingTerminalOpenOpen (or focus) the floating terminal window
:FloatingTerminalCloseHide the window (buffer and shell sessions persist)
:FloatingTerminalToggleToggle open / close - bind this to your keymap
:FloatingTerminalResizeRecompute geometry and reposition the window
:FloatingTerminalNew [name]Create a new named session (auto-named if no argument)
:FloatingTerminalCloseSessionTerminate the active session and remove its buffer
:FloatingTerminalNextSwitch to the next session in the tab bar
:FloatingTerminalPrevSwitch to the previous session in the tab bar
:FloatingTerminalSwitch {n}Jump directly to session by index
:FloatingTerminalRename [name]Rename the current session (prompts if no argument)

Configuration Options

OptionTypeDefaultDescription
widthnumber0.8Fraction of editor width
heightnumber0.8Fraction of editor height
borderstring"rounded"Border style: single, double, rounded, solid, shadow
start_in_insertbooltrueEnter insert mode when opening
create_user_commandbooltrueRegister :FloatingTerminal* commands
create_keymapbooltrueRegister the toggle keymap
keymapstring"<leader>tt"Key to toggle the terminal
close_on_job_exitbooltrueAuto-close window when shell exits
titlestring"Terminal"Window title (Neovim 0.9+)
session_name_prefixstring"Terminal"Prefix for auto-generated session names
keymap_newstring"<A-n>"Create a new terminal session
keymap_close_sessionstring"<A-x>"Close the active session; "" or nil to disable
keymap_nextstring"<A-l>"Switch to the next session
keymap_prevstring"<A-h>"Switch to the previous session
keymap_renamestring"<A-r>"Rename the current session interactively


~297
Lines of Lua
1
File
0
Dependencies
0.8+
Neovim req.

07Installation & Usage

Lua

{ "lambertse/nvim-toggleterm", config = function() require("nvim-toggleterm").setup({ width = 0.8, height = 0.8, border = "rounded", start_in_insert = true, create_user_command = true, create_keymap = true, keymap = "<leader>tt", close_on_job_exit = true, title = "Terminal", -- multi-session session_name_prefix = "Terminal", keymap_new = "<A-n>", keymap_close_session = "<A-x>", keymap_next = "<A-l>", keymap_prev = "<A-h>", keymap_rename = "<A-r>", }) end, }

With the default config, toggle the terminal with <leader>tt or via :FloatingTerminalToggle. The shell opens in insert mode. Exit to normal mode with Ctrl-\ Ctrl-n, then close or toggle the window. Press Alt-n to open a second session, Alt-l / Alt-h to navigate between them, and Alt-r to rename the active one. macOS users may need to configure Alt key forwarding in iTerm2 or Kitty.


08Future Work

βœ“
Multiple named terminals
Open a "test" terminal and a "server" terminal independently, each with their own buffer. Navigate via winbar tab bar or Alt keymaps.
β—‹
Send-to-terminal API
Pipe lines or visual selections from a buffer into the floating terminal - without duplicating built-in Neovim features.
β—‹
Status line integration
Show whether a terminal session is currently active in lualine or heirline.
βœ“
Buffer reuse
The shell session, history, and working directory persist across toggles.
βœ“
Auto-resize on VimResized
Window always stays proportional when the editor is resized.
βœ“
Zero dependencies
Pure Lua, one file, ships with Neovim 0.8+ - no extra setup.

One file. Multiple sessions.
Install it, bind <leader>tt, and never leave Neovim to open a terminal again. Spin up a second session with Alt-n, switch with Alt-l/h - all in one floating window.
Related Projects
⏱
NeoVim Pomodoro Timer
Pomodoro timer with IPC sync across Neovim instances
πŸƒ
VN Card Board Scorer
Mobile-first score tracker for TiαΊΏn LΓͺn
On this page
Loading...
N

Subscribe to my newsletter

Get notified when I publish new posts on my blog

Β© 2026 Minh-Tri Le. All rights reserved.