# vim-plug: a one file plugin manager that installs itself from master

> vim-plug is a single Vim script file that installs with one curl command, tracks plugins through a begin and end block in your vimrc, and pulls from the master branch rather than a pinned release. Small enough to read in full, thin on version pinning.

**junegunn/vim-plug** — :hibiscus: Minimalist Vim Plugin Manager

- Repository: https://github.com/junegunn/vim-plug
- Website: https://junegunn.github.io/vim-plug/
- Stars: 35,775 · Forks: 1,934
- Language: Vim Script
- License: MIT
- Published: 2026-08-17 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/junegunn-vim-plug

## The curl command installs from master, not from the 0.14.0 release

vim-plug is a plugin manager that happens to be one Vim script file. No binary, no build step, no package manager step. The documented install is a curl against the raw GitHub URL, and the --create-dirs flag creates the autoload directory for you:

```sh
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
```

Two details in that URL deserve a close read. It names the master branch, so the file you get is whatever master holds at the moment of the fetch, and the :PlugUpgrade command runs the same fetch again later. The newest tagged release is 0.14.0 from 2024-07-09, with 0.13.0 and 0.12.0 both landing in March 2024, while the last push to the repository was on 2026-05-22. The project is not archived. So you are installing a moving branch, and the release tags sit two years behind that branch, which means this install path gives you no fixed version of the manager itself if you need one.

## plug#end() quietly switches on filetype detection and syntax

Once plug.vim is in place, vim-plug reads one block out of your vimrc or init.vim:

```vim
call plug#begin()

" List your plugins here
Plug 'tpope/vim-sensible'

call plug#end()
```

Reload the file or restart Vim and four commands carry the work. :PlugInstall installs what is listed, :PlugUpdate installs or updates it, :PlugDiff shows the changes from the last update, and :PlugClean removes plugins you have dropped from the list. One side effect arrives with the block itself. plug#end() also executes filetype plugin indent on and syntax enable, so the manager turns on filetype detection and syntax highlighting for the entire editor without asking you. Two lines placed after the call put your old behavior back:

```vim
call plug#end()
filetype indent off   " Disable file-type-specific indentation
syntax off            " Disable syntax highlighting
```

Worth doing at the start rather than after a plugin starts mis-indenting a file.

## Neovim, Flatpak and Windows each look in a different autoload directory

The command above assumes ~/.vim. Every other setup has its own path, and the paths differ far more than the download does. Neovim on Unix and Linux reads the file from its site directory under XDG_DATA_HOME, falling back to ~/.local/share:

```sh
sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
       https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
```

A Flatpak install has a separate data root and ignores XDG entirely:

```sh
curl -fLo ~/.var/app/io.neovim.nvim/data/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
```

Windows drops the dot and uses vimfiles, and the PowerShell call carries no directory creation flag, so the destination has to exist before you run it:

```powershell
iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |`
    ni $HOME/vimfiles/autoload/plug.vim -Force
```

Neovim on Windows resolves the same choice inside a single expression, taking XDG_DATA_HOME when it is set and LOCALAPPDATA when it is not:

```powershell
iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |`
    ni "$(@($env:XDG_DATA_HOME, $env:LOCALAPPDATA)[$null -eq $env:XDG_DATA_HOME])/nvim-data/site/autoload/plug.vim" -Force
```

The consequence for anyone running two editors: a manager installed for Vim is invisible to Neovim, and one installed for Neovim never loads in Vim, with no error to tell you which case you are in. Run the install command once per editor and confirm the path before you start hunting for a missing :Plug command.

## Shallow clones buy disk space and give back git history

Three speed claims come with a cost attached to each. Shallow clones minimize disk space and download time, but a shallow clone holds only the commits needed to reach the ref you asked for, so the history inside a plugged directory stops there. A git log or a bisect against a plugin directory will not walk back to the start of that project, and no documented option deepens a clone after the fact. On-demand loading keeps plugin files out of the startup path, which is where the faster startup claim comes from, and the price is that an edit to a plugin's own filetype detection or syntax file can have no visible effect until you restart the editor. Parallel installation and update is the third, illustrated by a demo clip linked under the name 40-in-4.gif. Read that as a demonstration rather than a measurement: it says nothing about your machine, your network or your plugin set, and the startup figure points at a separate benchmark repository instead of a result published here.

## The Plug options move the ref, the subdirectory, the name and the location

A Plug line can change which ref you track, which subdirectory holds the plugin code, what the plugin is called, and where it lands on disk. The Lua form in Neovim shows most of the range:

```lua
local vim = vim
local Plug = vim.fn['plug#']

vim.call('plug#begin')

-- Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
Plug('fatih/vim-go', { ['tag'] = '*' })

-- Using a non-default branch
Plug('neoclide/coc.nvim', { ['branch'] = 'release' })

-- Use 'dir' option to install plugin in a non-default directory
Plug('junegunn/fzf', { ['dir'] = '~/.fzf' })
```

The shorthand form expands a GitHub owner and name into a URL, and any valid git URL is accepted, so a plugin hosted elsewhere takes the same line. Two warnings in the Vim script form matter more than they look. Use single quotes, and keep a custom plugin directory away from standard Vim directory names such as 'plugin', because a plugin dropped there is sourced as part of the editor's own runtime. The wildcard tag needs git 1.9.2 or newer, which is the only version floor anywhere in the install path.

The dir option is where the sharp edge sits. Moving a plugin to a path like ~/.fzf is convenient when something outside the editor also needs that code, and it also takes the plugin out of the directory the manager tracks. The reverse case is worse: a directory you cloned by hand into the default plugin root is unlisted as far as vim-plug is concerned, so :PlugClean deletes it, and the bang form deletes it without a prompt. Know which directories are yours before you run :PlugClean! on a machine you did not set up.

## PlugSnapshot produces a script, not a lockfile the manager reads back

The command list has seven entries: PlugInstall, PlugUpdate, PlugClean, PlugUpgrade, PlugStatus, PlugDiff and PlugSnapshot. Install and update both accept an optional list of plugin names and a #threads argument for parallel work. Nothing in that list records which version of a plugin you are running, and nothing reads such a record back on the next install. PlugSnapshot comes closest, and its description is careful about what it produces: a script for restoring the current snapshot of the plugins. You generate the script, you keep it, you run it yourself. A bare Plug line with no branch, tag or commit resolves to the default branch tip at the moment of the install, so restoring on another machine, or on the same machine a month later, can hand you different code than what you reviewed. The features list also names post-update hooks and support for externally managed plugins, which tells you both exist, but no hook example appears in the README, so the shape of a hook is something you have to look up in the source.

## Where vim-plug stops and lazy.nvim starts

The design premise here is that the manager and the plugin list are both plain Vim script in files you already keep in version control, and that the one file works in Vim and in Neovim alike, a claim the README extends back to every Vim since 2006 and every Neovim release. lazy.nvim takes a different route. It is a Lua plugin for Neovim, the configuration is written in Lua rather than Vim script, and each plugin entry is the place where you declare how that plugin should be loaded and which version you want.

That single difference decides the choice for most people. If you want lazy loading rules, per plugin version pins or a plugin dependency graph, vim-plug has nowhere to put them: the Plug line takes a ref, a subdirectory, a name and a directory, and the command list has no command that consumes a manifest. If you want one file with no dependencies and a config you can read top to bottom without knowing Lua, then lazy.nvim's approach is the heavier of the two. The cost of the simpler design surfaces at update time, where :PlugUpdate resolves every unpinned line to whatever its branch holds that day and the decision about what to run next is yours alone.

## Conclusion

vim-plug fits people who want one file, no dependencies, and a config they can read end to end, and who accept that :PlugUpdate pulls whatever a default branch holds at that moment. It does not fit a setup that needs per plugin version pins across machines, declared lazy loading rules or a plugin dependency graph, because none of those exist in its command list. Before switching, run :PlugSnapshot on a config you already like and read the generated script: if that script means nothing to you, neither will the manager.

## FAQ

### What is vim-plug?

It is a Vim plugin manager whose entire implementation is one script file, plug.vim, with no dependencies. You drop that file into your autoload directory and declare plugins in a block that runs from call plug#begin() to call plug#end().

### How do I install vim-plug?

For Vim on Unix, a single curl command writes plug.vim into ~/.vim/autoload and the --create-dirs flag makes the directory. The URL points at the master branch, so the file you get is whatever master holds when the command runs.

### How do I install vim-plug on Windows?

In PowerShell, iwr downloads the same master branch file and New-Item writes it to $HOME/vimfiles/autoload/plug.vim with the -Force flag. That command has no directory creation option, so the autoload directory has to exist first.

### How do I use vim-plug in Neovim?

The manager file goes to nvim/site/autoload/plug.vim under XDG_DATA_HOME, or ~/.local/share when that variable is unset. Plugin declarations can be written in Vim script in init.vim, or in Lua as init.lua using vim.fn['plug#'] and vim.call('plug#begin').

### How do I remove a plugin with vim-plug?

Delete its Plug line from the config block, then run :PlugClean, which removes plugins that are no longer listed. Adding the bang, as :PlugClean!, removes them without the confirmation prompt.

### Is vim-plug or lazy.nvim the better fit for Neovim?

vim-plug is one Vim script file that works in both editors and takes no Lua, while lazy.nvim is a Neovim Lua plugin where each entry carries its own load rule and version pin. Choose vim-plug for the smaller config, and lazy.nvim if you need those per plugin settings declared in one place.

## Sources

- [Official documentation](https://junegunn.github.io/vim-plug/)
- [Official README](https://github.com/junegunn/vim-plug#readme)
- [Project repository](https://github.com/junegunn/vim-plug)
- [Release notes](https://github.com/junegunn/vim-plug/releases)

---

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