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
Minh-Tri Le
Minh-Tri Le
March 19, 2026 · 6 min read

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

The Window
04
  • Floating & centered
    Opens over your code - no split, no tab, no layout shift.
  • Proportional sizing
    Width and height set as fractions of the editor dimensions.
  • Six border styles
    single, double, rounded, solid, shadow, or a custom set.
  • Auto-resize
    Re-centers and rescales on VimResized, always proportional.
Sessions
04
  • Multiple named terminals
    Each session keeps its own buffer, shell, history, and cwd.
  • Winbar tab bar
    All active sessions listed; the current one is highlighted.
  • Full session control
    Create, rename, close, and switch via commands or buffer-local keymaps.
  • Auto-close on exit
    When the shell job ends the window closes - configurable.
Setup
04
  • Optional user commands
    The :FloatingTerminal* family, off if you prefer pure keymaps.
  • Generated keymaps
    One config line binds the toggle key - or bind it yourself.
  • Starts in insert mode
    start_in_insert drops you straight at the prompt.
  • Zero dependencies
    Pure Lua, ships against Neovim 0.8+. Titles need 0.9+.

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.
View on GitHub
Read More

Pomodoro Timer

A Pomodoro timer living inside Neovim - written in pure Lua, zero dependencies, synced across sessions via IPC.

By Minh-Tri Le - Neovim Plugins

iBridge - IPC messaging & RPC framework

An IPC/RPC framework that bridges processes across language boundaries - a C++ server and a JavaScript client talking to each other over Unix domain sockets and Protocol Buffers, with no shared memory and no native bindings.

By Minh-Tri Le - Side Projects

Vietnamese Card Board Scorer

A mobile-first score tracker for Vietnamese card games like Tiến Lên. Born on the first day of Tết, built to replace paper and pen.

By Minh-Tri Le - Funny Projects

Understanding MachO Binary Format

High level architecture of a Mach-O binary. Learned through implementing application support parsing iOS binary.

By Minh-Tri Le - Mar 2025
N

Subscribe to my newsletter

Get notified when I publish new posts on my blog

© 2026 Minh-Tri Le. All rights reserved.