# nvim-surround: add, change and delete delimiter pairs in Neovim

> nvim-surround is a Lua plugin for Neovim 0.8+ that wraps, rewrites and strips surrounding pairs with three keymaps. It is the modern reimplementation of the vim-surround idea, and the trade-offs sit in its configuration surface rather than its keymaps.

**kylechui/nvim-surround** — Add/change/delete surrounding delimiter pairs with ease. Written with :heart: in Lua.

- Repository: https://github.com/kylechui/nvim-surround
- Stars: 4,305 · Forks: 79
- Language: Lua
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/kylechui-nvim-surround

## The editing problem nvim-surround solves

Editing text is mostly a sequence of small structural edits: a word needs quotes around it, a call needs parentheses, a paragraph stops being an HTML tag and becomes a Markdown link. Doing that by hand means moving to one end of the region, inserting a character, moving to the other end, inserting the matching character, and then fixing the spacing. nvim-surround collapses each of those sequences into a single operator-style command.

The plugin targets Neovim users who already think in operators and motions. The README lists three core operations, add, delete and change, bound to ys{motion}{char}, ds{char} and cs{target}{replacement}. If you have used tpope's vim-surround, the mental model transfers directly. What is different is the implementation language and the extension points: nvim-surround is written in Lua and exposes configuration through require("nvim-surround").setup, with buffer-local variants available through buffer_setup.

It is not a general text-object plugin. It does one job, and the README's feature list stays inside that job: pairs, function calls, HTML tags, dot-repeat, buffer-local mappings and surrounds, nearest-pair jumping, single-character aliases, user-input-driven pairs, custom surrounds and selection highlighting.

## How the add, delete and change operations work

The README's usage table is the clearest description of the mechanism. With the cursor marked by *, ysiw) turns surr*ound_words into (surround_words). The operator ys takes a motion (iw, $, and so on) and a target character, then wraps the covered region. The same table shows ys$ wrapping to the end of the line and ysiw( producing ( surround_words ) instead, because surrounding with an opening delimiter adds a space before and after the selection while the closing delimiter does not.

Deletion and change read the other way around. ds] removes the brackets around the cursor, dst removes the enclosing HTML tag, and dsf removes the enclosing function call. For change, cs'" rewrites single quotes as double quotes, and csth1<CR> retypes a tag as h1. The target is resolved to the nearest enclosing pair, which the feature list calls out explicitly: the plugin jumps to the nearest surrounding pair for modification.

Two design details matter in practice. The first is aliasing. A single character can stand in for several text-objects, so the README notes that q is aliased to `, ', and ", which makes csqb replace the nearest set of quotes with parentheses. The second is that custom surrounds are not limited to literal characters. The README states first-class support for Vim motions, Lua patterns and Tree-sitter nodes, so a surround can be defined by a pattern or by a syntax node rather than by a fixed delimiter. That is where the plugin's real configuration cost lives.

## Installing nvim-surround and using it for the first time

The README lists Neovim 0.8 or newer as the requirement, and recommends nvim-treesitter-textobjects if you want to define surrounds from Tree-sitter text-objects. Installation is done through a plugin manager. With lazy.nvim, the README gives this spec, which pins the version range for stability:

```lua
{
    "kylechui/nvim-surround",
    version = "^4.0.0", -- Use for stability; omit to use `main` branch for the latest features
    event = "VeryLazy",
    -- config = function()
    --     require("nvim-surround").setup({
    --         -- Put your configuration here
    --     })
    -- end
}
```

If you are on Neovim 0.12 or newer, the README also documents the built-in vim.pack manager. Note the comment in that snippet, which states the version requirement plainly:

```lua
-- NOTE: This requires Neovim version 0.12 and greater!
vim.pack.add({ {
    src = "https://github.com/kylechui/nvim-surround",
    version = vim.version.range("4.x"), -- Use for stability; omit to use `main` branch for the latest features
} })
```

The README also notes a LuaRocks package, so luarocks install nvim-surround is another route if you manage Neovim plugins that way. After the plugin loads, no setup call is strictly required; the README says to see :h nvim-surround.configuration for configuration and :h nvim-surround.setup for setup details.

A first real use is the sequence from the usage table. Put the cursor inside a word and type ysiw) to wrap it in parentheses, then type cs)" to turn those parentheses into double quotes, then ds" to remove them. If the result is not what you expect, the README points at :h nvim-surround.usage for the detailed reference rather than the README itself.

## Where nvim-surround is the wrong tool

The README states the requirement as Neovim 0.8+, and that is a hard boundary rather than a suggestion. The plugin is Lua and calls Neovim's Lua API, so it does not run in Vim. If your editing still happens in Vim, or in a Neovim build old enough to predate 0.8, this is not a plugin you can adopt at all, and the README offers no fallback path.

The second boundary is scope. nvim-surround is a single-purpose plugin. If you already run a distribution or a collection that bundles its own surround implementation, adding this one means two implementations of the same keymaps competing for the same prefixes. That is a configuration problem rather than a bug, but it is one you have to resolve deliberately.

The third is the configuration surface. The README describes custom surrounds built from Vim motions, Lua patterns and Tree-sitter nodes, which is genuinely more expressive than a list of literal character pairs. It is also more to get wrong. The README does not document rollback or a migration path between major versions, and it does not describe what happens when a custom surround's pattern fails to match. If you need a surround implementation you can configure once and forget, the default character pairs are the safe subset; the pattern and node based surrounds are the part that requires reading :h nvim-surround.configuration before you commit to them.

## nvim-surround compared with vim-surround and mini.surround

The README names its predecessors directly in a shoutouts section: vim-surround, mini.surround and vim-sandwich. The difference in approach is worth stating plainly, because it decides which one you install.

vim-surround is the original Vimscript plugin and the source of the ys, ds and cs keymap vocabulary that nvim-surround reuses. If you split time between Vim and Neovim, or maintain a shared configuration, vim-surround is the version that runs in both. nvim-surround gives that up in exchange for Lua and for the extension points the README lists: Tree-sitter node surrounds, Lua patterns, and buffer-local mappings and surrounds.

mini.surround is part of the mini.nvim collection, which means it arrives as one module of a larger set rather than as a standalone dependency. The practical difference is dependency shape: nvim-surround is one repository you add and one module you configure, while mini.surround is configured as part of mini.nvim's own setup conventions. Neither is better in the abstract. If you already use mini.nvim, adding nvim-surround means a second surround implementation; if you use neither, the choice is between a focused plugin and a collection.

vim-sandwich is the third name on the list, and the README does not describe how its approach differs, so treat that comparison as unverified from the README alone.

## Maintenance, releases and what the MIT licence means here

The repository is not archived, and the last push was on 2026-06-08. The most recent release listed is v4.0.5 from 2026-05-02, preceded by v4.0.4 on 2026-03-07 and v4.0.3 on 2026-02-25. That is a steady release cadence across the 4.x line, and the README's installation snippets pin to that major line with ^4.0.0 and vim.version.range("4.x") for stability, explicitly noting that omitting the version tracks main for the latest features.

Upgrade cost is mostly a function of which version constraint you choose. Pinned to 4.x, upgrades are patch and minor releases within a documented line. Tracking main means you take unreleased changes, and the README frames that as the trade for latest features without describing what can break. There is a CHANGELOG.md at the top level of the repository, which is where release-to-release detail would live; the README does not summarize it.

The licence is MIT. In practical terms that permits use, modification and redistribution with the licence and copyright notice retained. This is not legal advice, and if you redistribute the plugin inside a larger product, the notice requirement is the part to check against your own distribution process.

## Conclusion

Adopt nvim-surround if you already run Neovim 0.8 or newer and want add/change/delete on delimiter pairs with dot-repeat and Tree-sitter aware custom surrounds. Skip it if you are on Vim, or if you want a surround implementation that ships inside a larger plugin collection rather than as a standalone dependency. Before committing, verify two things: that your Neovim reports 0.8 or higher, since the README lists that as the requirement, and that your plugin manager pins a tag such as ^4.0.0 rather than tracking main, because the README notes that omitting the version uses main for the latest features.

## FAQ

### How do I use nvim-surround to add, change and delete pairs?

The README binds the three core operations to ys{motion}{char} for add, ds{char} for delete and cs{target}{replacement} for change. A worked example from its usage table is ysiw) to wrap the word under the cursor in parentheses, and cs'" to rewrite single quotes as double quotes.

### What is the difference between vim-surround and nvim-surround?

vim-surround is the original Vimscript plugin, and nvim-surround reuses its ys, ds and cs keymap vocabulary. nvim-surround is written in Lua, requires Neovim 0.8 or newer, and adds extension points the README lists as first-class support for Vim motions, Lua patterns and Tree-sitter nodes.

### What is the difference between nvim-surround and mini.surround?

The README lists mini.surround among related projects without describing its internals. The structural difference visible from the README is packaging: nvim-surround is a standalone repository you install and configure on its own, while mini.surround is a module of the mini.nvim collection.

### What are the alternatives to nvim-surround?

The README's shoutouts section names vim-surround, mini.surround and vim-sandwich as related projects. It does not describe how vim-sandwich's approach differs, so that comparison cannot be made from the README alone.

### How do I use surround in Vim?

nvim-surround is a Neovim plugin and the README lists Neovim 0.8 or newer as its requirement, so it does not run in Vim. The README names vim-surround, the original Vimscript plugin, as a related project; nvim-surround reuses its ys, ds and cs keymap vocabulary.

## Sources

- [Issues](https://github.com/kylechui/nvim-surround/issues)
- [kylechui/nvim-surround on GitHub](https://github.com/kylechui/nvim-surround)
- [License: MIT](https://github.com/kylechui/nvim-surround/blob/main/LICENSE)
- [README](https://github.com/kylechui/nvim-surround/blob/main/README.md)
- [Releases](https://github.com/kylechui/nvim-surround/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kylechui-nvim-surround
