# mini.surround: dot-repeatable surround editing for Neovim

> mini.surround is a Neovim Lua plugin that adds, deletes, replaces, finds and highlights surrounding pairs, with builtins for brackets, function calls, tags and prompts, and search behaviour configurable enough to reach a previous or nearest pair. It suits editors who want surround handling that behaves like normal text editing, and it depends on how you read the one-character input model.

**nvim-mini/mini.surround** — Neovim Lua plugin with fast and feature-rich surround actions. Part of 'mini.nvim' library.

- Repository: https://github.com/nvim-mini/mini.surround
- Stars: 720 · Forks: 19
- Language: Lua
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/nvim-mini-mini-surround

## What mini.surround does, and what it deliberately leaves out

mini.surround is a Neovim Lua plugin with fast and feature-rich surround actions, part of the mini.nvim library. The action list is short and complete: add, delete, replace, find and highlight surrounding, such as a pair of parenthesis or quotes.

The mechanics are stated plainly in the feature list. The text editing actions are dot-repeatable out of the box and respect a count, so sa followed by a dot repeats the wrap and a count changes how many times it applies. There are builtins for brackets, function call, tag, user prompt, and any alphanumeric, punctuation or whitespace character.

There is also a maintained configuration similar to tpope/vim-surround, which is the reference point most people arriving at this plugin will have in mind, and a comparison worth making explicit. vim-surround is a visual-mode plugin: you select the text, then press a key to say what should go around it. mini.surround is a character-driven plugin: you press sa and then type a character that means both what to look for and what to add, and the plugin finds the pair itself. That difference in interaction model is the single most important thing to understand before configuring anything.

The alternative is not that mini.surround has no equivalent, but that the two styles suit different habits. If your muscle memory says select then surround, the character-driven model will feel wrong at first. If you have been using treesitter-based or operator-pending plugins, the single character input is closer to what you already know.

## One character means two things: input and output

The central design decision is that surrounding is identified by a single character which acts as both input and output. The readme spells out what that means for each builtin, and the cases are worth reading one by one.

For a function call, the character f means a string of alphanumeric symbols or an underscore or a dot followed by balanced parentheses. In input position it finds a function call, and in output position it prompts the user to enter the function name. So sa f wraps the current expression as a call of a name you type, and sd f deletes the call wrapper around it.

The tag character t behaves the same way with different semantics. In input it finds a tag with the same identifier, and in output it asks for the tag name.

All the symbols in brackets, meaning parentheses, square brackets, curly braces and angle brackets, are handled as a group. In input they represent balanced brackets, with the open one padded with whitespace and the close one without. In output they give the left and right parts of the brackets. The interactive character question mark prompts the user to enter both the left and right parts.

And every other single character identifier, limited to those supported by getcharstr(), means a surrounding with identical left and right parts. That is how quotes work: type a double quote and you get a pair of double quotes.

The consequence is that the input alphabet is a flat namespace of single characters, which is compact and fast, and also means some characters are spoken for. The interactive question mark is the escape hatch for anything the scheme does not cover.

## Searching: previous, next, nearest, and the 20-line window

Finding the surrounding is where most surround plugins become unpredictable, and mini.surround makes that part configurable.

Search methods are configurable so that the plugin can find not only the covering surrounding but possibly the next, previous or nearest one. The documentation points to MiniSurround.config for the details, and the readme makes a further point that all the actions involving finding surrounding, which means delete, replace, find and highlight, can be used with a suffix that changes the search method to find the previous or last one.

The default config shows the shape of that. Two suffix keys are defined, suffix_last set to l and suffix_next set to n, with the comments noting that suffix_last is the suffix to search with the prev method and suffix_next the one for the next method. So the default keymaps are sf and sF to find a surrounding to the right or to the left, and the suffix variants let you reach beyond the current one.

The other important knob is n_lines, the number of lines within which surrounding is searched, set to 20 by default. That is a real constraint rather than a tuning detail. A surround pair that spans more than twenty lines, which happens with a long function call argument list, a long SQL string, or a deeply nested expression, will simply not be found by the default configuration. Users who work with large blocks will need to raise it and will pay a slightly higher search cost.

The mapping table is also part of the search story, because each action has its own key and the keys are configurable. The defaults are add mapped to sa, delete to sd, find to sf, find_left to sF, highlight to sh and replace to sr, and the config comment points out that using an empty string disables a mapping, so the plugin can be made to take over only the actions you want.

## Installing it: three package managers, two branches, one setup call

The plugin installs either as part of the mini.nvim library, which is the recommended route, or as a standalone Git repository, and the readme gives snippets for three package managers.

On Neovim 0.12 and newer, vim.pack is the built-in option, and the standalone plugin is added with:

```lua
vim.pack.add({ 'https://github.com/nvim-mini/mini.surround' })
```

Before Neovim 0.12, mini.deps is the route, and the standalone call is:

```lua
add('nvim-mini/mini.surround')
```

With lazy.nvim, the standalone spec is:

```lua
{ 'nvim-mini/mini.surround', version = false },
```

In all three cases the readme ends with the line that people skip and then wonder about: do not forget to call require('mini.surround').setup() to enable its functionality. The plugin ships sensible defaults, so the setup call can be empty, but it has to happen.

The branch choice is the other decision. The main branch, which is the default, has the latest development version, and all changes since the last stable release should be perceived as being in beta testing phase, meaning they already passed alpha testing and are moderately settled. The stable branch is updated only upon releases with code tested during the public beta-testing phase in main. If you want the plugin as part of the whole mini.nvim library, follow the library's own installation instructions instead of the standalone snippets.

There is a Windows-specific trap in the readme: if you are on Windows there might be problems with too long file paths, showing up as an error saying it is unable to create a file because the filename is too long. The two suggested fixes are enabling the git global config value core.longpaths and reinstalling, or installing the plugin in another place with a shorter path.

## The default config, and the option that lets you define your own pairs

The default configuration is documented in full, and the readme says there is no need to copy it inside setup() because it will be used automatically. Reading it tells you what the plugin considers tunable.

The custom_surroundings option is an empty table by default, and the comment describes it as custom surroundings to be used on top of the builtin ones, with more information and examples in the MiniSurround.config help. This is the escape hatch for the flat single-character namespace, and the readme's feature list also mentions customization via Lua patterns or functions, so a custom surrounding can be defined either by a pattern that recognises existing text or by a function that computes the pair.

The highlight_duration option is 500 milliseconds, the duration of the highlight when calling MiniSurround.highlight(). The mappings table holds the six action keys plus the two search suffixes, with the comment reminding you that an empty string disables a mapping.

And n_lines, 20 by default, is the number of lines within which surrounding is searched.

The last option in the visible config is about selection type, and it is cut off in the published text, with the comment reading whether to respect selection type and the first sub-bullet about placing surroundings. The documentation file doc/mini-surround.txt is where the full text lives, and the readme points to it for details on the features and configuration. The repository itself is small: LICENSE, README.md, a doc/ directory and a lua/ directory, with no build step, no compiled artefacts and no runtime dependencies beyond Neovim itself.

## Where the plugin has moved, and where contributions belong

There is a governance note in the readme that says more about the project's future than any changelog entry.

The plugin was previously hosted at a personal echasnovski GitHub account, and it was transferred to a dedicated organization to improve long term project stability, with a link to the discussion explaining the details. The repository is nvim-mini/mini.surround, and the last push was on 2026-10-13, with no GitHub releases published. The library is MIT licensed.

The more important part of that note is the contribution rule: all contributions, issues, pull requests and discussions, are done inside mini.nvim. So the standalone repository is where you read and install, and the parent library is where you report problems and propose changes. That has a practical consequence for anyone filing a bug, and it is worth knowing before you spend an evening reproducing a problem in the standalone clone.

The readme also points at two documentation resources beyond the help file. There is a full config example, MiniMax, that uses this module, which is the fastest way to see how the defaults are meant to be combined with the rest of the library. And the whole library documentation covers the general design principles, disable and configuration recipes, and more.

For someone evaluating this plugin, the honest summary is that it is small, dependency-free, and part of a library with a clear structure, and that the parts you will actually spend time on are the search method configuration and the custom surroundings table.

## Conclusion

mini.surround fits a Neovim user who wants surround handling that is dot-repeatable, configurable per surrounding, and consistent with the rest of the mini.nvim set, and it does not fit someone who wants a visual selection of the pair to surround. Before adopting it, read the doc/mini-surround.txt help file, because the single-character input and the search-method suffixes are the two things you will be configuring, and check whether the n_lines search window of 20 lines suits the size of the expressions you edit.

## FAQ

### What does mini.surround do in Neovim?

It adds, deletes, replaces, finds and highlights surrounding pairs such as parentheses and quotes, with builtins for brackets, function calls, tags, user prompts and any alphanumeric, punctuation or whitespace character. The text editing actions are dot-repeatable out of the box and respect a count.

### How do I install mini.surround as a standalone plugin?

With vim.pack on Neovim 0.12 and newer, use vim.pack.add({ 'https://github.com/nvim-mini/mini.surround' }); with mini.deps use add('nvim-mini/mini.surround'); with lazy.nvim use { 'nvim-mini/mini.surround', version = false }. In every case you must call require('mini.surround').setup() to enable it.

### How do I make mini.surround find the previous or next surrounding pair?

All actions that find a surrounding, which are delete, replace, find and highlight, can be used with a suffix that changes the search method. The default config defines suffix_last as l for the prev method and suffix_next as n for the next method, and the search methods themselves are configurable through MiniSurround.config.

### What is the difference between the main and stable branches of mini.surround?

The main branch, which is the default, has the latest development version, and changes since the last stable release are described as being in a beta testing phase, already past alpha testing and moderately settled. The stable branch is updated only upon releases with code tested during the public beta-testing phase in main.

### Where do I report bugs or contribute to mini.surround?

All contributions, including issues, pull requests and discussions, are done inside the mini.nvim library rather than in the standalone mini.surround repository, which was transferred from a personal account to the nvim-mini organization to improve long term project stability.

## Sources

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

---

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