or simply
August 26, 2026 · View on GitHub
ZPM - Zsh Plugin Manager
Fastest, configurable and extensible zsh plugin manager
Zpm is a plugin manager for ZSH who combines the imperative and declarative approach. At first run, zpm will do complex logic and generate cache, after that will be used cache only, so it makes this framework to be very fast.
Features
- Speed. Fastest plugin manager (Really, after the first run, zpm will not be used at all)
- Compatibility. Zpm plugins are compatible with oh-my-zsh
- Portability. Zpm runs on Linux, Android, OpenWrt, FreeBSD and macOS
- Support for async loading
- Dependencies between packages
- Hooks
- Function autoloading
- Extensible
- Possibility to use github/gitlab/bitbucket mirrors (useful for China)
Table of Contents
- Features
- Table of Contents
- Stats
- Base dependences
- Installation
- CLI Commands & Aliases
- How to use
- Cookbook & Common Examples
- Configuration & Environment Variables
- Guide for Plugin Authors
- Troubleshooting
- Developing process
- TODO
- Changelog
Stats
ZPM achieves near-instant shell startup by combining all loaded plugins and configurations into a single byte-compiled cache (zpm-cache.zsh.zwc) and asynchronous runner (zpm-cache-async.zsh.zwc). On subsequent shell startups, no plugin manager logic or filesystem lookups are executed—only the pre-compiled cache is sourced.
| Plugin Manager / Setup | Cold Start / Initialization | Warm Interactive Startup (50+ plugins) |
|---|---|---|
| ZPM | ~0.8s – 2.0s (first run only) | ~0.006s – 0.050s (compiled cache) |
| Zinit (Turbo mode) | ~0.5s – 1.5s | ~0.100s – 0.150s |
| Oh-My-Zsh | N/A (linear sourcing) | ~0.200s – 0.450s |
| Antigen | ~1.0s – 3.0s | ~0.250s – 0.500s |
Benchmark details on Intel I7-8750H, SanDisk SD7SN6S, 16GB RAM
zsh -i -c exit 0.00s user 0.00s system 102% cpu 0.006 total
zsh -i -c exit 0.01s user 0.00s system 101% cpu 0.006 total
zsh -i -c exit 0.00s user 0.01s system 99% cpu 0.006 total
zsh -i -c exit 0.01s user 0.00s system 102% cpu 0.007 total
zsh -i -c exit 0.00s user 0.00s system 100% cpu 0.007 total
zsh -i -c exit 0.01s user 0.00s system 100% cpu 0.007 total
zsh -i -c exit 0.00s user 0.00s system 101% cpu 0.007 total
zsh -i -c exit 0.00s user 0.00s system 100% cpu 0.006 total
zsh -i -c exit 0.00s user 0.00s system 101% cpu 0.007 total
zsh -i -c exit 0.00s user 0.00s system 100% cpu 0.008 total
Test on Raspberry Pi Zero W, Raspbian 10, 1GHz Broadcom BCM2835 ARMv6, 512MB RAM
zsh -i -c exit 0.14s user 0.05s system 85% cpu 0.219 total
zsh -i -c exit 0.14s user 0.05s system 43% cpu 0.436 total
zsh -i -c exit 0.14s user 0.05s system 58% cpu 0.325 total
zsh -i -c exit 0.12s user 0.07s system 90% cpu 0.206 total
zsh -i -c exit 0.15s user 0.05s system 84% cpu 0.231 total
zsh -i -c exit 0.15s user 0.04s system 46% cpu 0.407 total
zsh -i -c exit 0.13s user 0.06s system 62% cpu 0.306 total
zsh -i -c exit 0.11s user 0.08s system 83% cpu 0.227 total
zsh -i -c exit 0.14s user 0.05s system 47% cpu 0.403 total
zsh -i -c exit 0.11s user 0.08s system 62% cpu 0.307 total
Test on MikroTik RouterBOARD 951Ui-2HnD, OpenWrt 19.07.7, 600MHz Atheros AR9344 MIPS, 128MB RAM
zsh -i -c exit 0.09s user 0.03s system 83% cpu 0.144 total
zsh -i -c exit 0.10s user 0.02s system 29% cpu 0.412 total
zsh -i -c exit 0.10s user 0.02s system 69% cpu 0.173 total
zsh -i -c exit 0.10s user 0.03s system 73% cpu 0.165 total
zsh -i -c exit 0.10s user 0.02s system 81% cpu 0.150 total
zsh -i -c exit 0.10s user 0.02s system 71% cpu 0.170 total
zsh -i -c exit 0.10s user 0.02s system 85% cpu 0.141 total
zsh -i -c exit 0.10s user 0.02s system 42% cpu 0.283 total
zsh -i -c exit 0.11s user 0.02s system 68% cpu 0.176 total
zsh -i -c exit 0.10s user 0.02s system 75% cpu 0.161 total
With this set of plugins (51 total)
zpm-zsh/helpers
zpm-zsh/colors
zpm-zsh/tmux
zpm-zsh/vte
zpm-zsh/core-config
zpm-zsh/ignored-users
zpm-zsh/check-deps
zpm-zsh/minimal-theme
zpm-zsh/material-colors
zpm-zsh/pr-is-root
zpm-zsh/pr-user
zpm-zsh/pr-return
zpm-zsh/pr-exec-time
zpm-zsh/pretty-time-zsh
zpm-zsh/pr-git
zpm-zsh/pr-cwd
zpm-zsh/pr-php
zpm-zsh/pr-rust
zpm-zsh/pr-node
zpm-zsh/pr-2
zpm-zsh/pr-eol
zpm-zsh/pr-zcalc
zpm-zsh/pr-correct
zpm-zsh/ls
zpm-zsh/colorize
zpm-zsh/ssh
zpm-zsh/dot
zpm-zsh/undollar
zpm-zsh/dropbox
lukechilds/zsh-better-npm-completion
zpm-zsh/clipboard
zpm-zsh/mysql-colorize
zpm-zsh/zshmarks
voronkovich/gitignore.plugin.zsh
zpm-zsh/autoenv
mdumitru/fancy-ctrl-z
zsh-users/zsh-history-substring-search
zdharma/fast-syntax-highlighting
zsh-users/zsh-autosuggestions
psprint/history-search-multi-word
zpm-zsh/zpm-readme
zpm-zsh/zpm-info
zpm-zsh/zpm-telemetry
zpm-zsh/zpm-link
@omz/extract
@omz/command-not-found
@omz/pip
@empty/npm
@empty/rustup
zpm-zsh/create-zsh-plugin
Base dependences
- zsh (>= 5.1)
- git
- One of these for parallel downloading:
- GNU Parallel
- Rush
- xargs (standard fallback)
- curl for remote downloads and GitHub Gists
- Termux for Android
- cli-markdown / cli-html / glow / bat — optional, for
zpm readmeterminal viewer
Installation
Add the following to your ~/.zshrc:
ZPM_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins/@zpm"
if [[ ! -f "${ZPM_DIR}/zpm.zsh" ]]; then
git clone --recursive https://github.com/zpm-zsh/zpm "${ZPM_DIR}"
fi
source "${ZPM_DIR}/zpm.zsh"
If you don't have a .zshrc, you can copy the provided example:
ln -sf "${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins/@zpm/zshrc" ~/.zshrc
CLI Commands & Aliases
| Command | Aliases | Description |
|---|---|---|
zpm load <plugins...> | (default) | Download and initialize specified plugins |
zpm upgrade [plugins...] | zpm u, zpm up | Upgrade all plugins (or specific plugins), then clear cache and reload |
zpm clean | zpm c, zpm cl | Remove generated ZPM cache ($ZSH_TMP_DIR) and restart shell |
zpm info [plugins...] | Display detailed metadata card for specified plugin(s) | |
zpm list | zpm ls | List all loaded/installed plugins |
zpm readme <plugin> | Display plugin's README in the terminal | |
zpm link [path] | Link and load a local directory or single script as a plugin | |
zpm if <condition> <command> | Execute command only if condition is true on first run | |
zpm if-not <condition> <command> | Execute command only if condition is false on first run | |
zpm <custom-command> | Execute custom subcommand function zpm-<custom-command> |
zpm load
Downloads, resolves, and loads one or more plugins into the current shell and registers them for inclusion in the pre-compiled startup cache.
zpm load zsh-users/zsh-autosuggestions
zpm load @omz/git @omz/extract
zpm upgrade (zpm u, zpm up)
Updates git repositories and remote plugins to their latest versions. Running without arguments updates all installed plugins (including @zpm itself). You can also pass specific plugin names to update only them:
zpm u # update all plugins
zpm u zsh-users/zsh-autosuggestions # update only a specific plugin
zpm clean (zpm c, zpm cl)
Purges the generated runtime cache in $ZSH_TMP_DIR (including byte-compiled cache and aggregated binary/completion files) and restarts the shell with exec zsh. Use this whenever you edit your ~/.zshrc.
zpm clean
# or simply
zpm c
zpm info
Displays detailed metadata for one or more plugins, including origin repository URL, plugin type, sync/async mode, local installation path, and current installation status:
zpm info zsh-users/zsh-autosuggestions
zpm info @zpm
zpm list (zpm ls)
Lists all plugins currently loaded or installed in your ZPM environment with their metadata:
zpm list
# or
zpm ls
zpm readme
Displays the README.md documentation for any installed plugin directly in your terminal. Automatically detects available terminal markdown renderers (cli-markdown, md, cli-html, glow, bat, or system $PAGER):
zpm readme zsh-users/zsh-autosuggestions
zpm readme @zpm
zpm link
Quickly symlinks and loads a local directory or standalone .zsh script into your current session (analogous to npm link). It automatically detects whether the target is a directory or single file, generates the appropriate @dir / @file spec, loads it immediately, and prints the exact snippet to add to your ~/.zshrc:
# In plugin's repository directory:
zpm link
# Or by providing a path:
zpm link ~/Projects/my-awesome-plugin
zpm link ~/.dotfiles/zsh/custom-aliases.zsh
Custom Commands Extension
You can easily extend ZPM with custom commands! Any shell function named zpm-<subcommand> will be automatically called when executing zpm <subcommand>:
function zpm-hello() {
echo "Hello from custom ZPM command! Arguments: $@"
}
zpm hello world
# Output: Hello from custom ZPM command! Arguments: world
How to use
Basic Syntax
zpm load [@type/]plugin-name[,tag1:val1,tag2,...]
Note: If you modify
~/.zshrc, apply your changes by runningzpm clean(orzpm c).
# plugin type (optional, defaults to @github)
# | plugin name
# | | tag
# | | | tag parameter(s)
# | | | separated by `:` boolean tag
# | | | | |
# ↓ ↓ ↓ ↓ ↓
zpm load @type/some/plugin,apply:source:path:fpath,async
Plugin Name & Types
| Type Prefix | Origin / Behavior | Example |
|---|---|---|
(none) or @github/ / @gh/ | Cloned from GitHub (default) | zpm load zsh-users/zsh-autosuggestions |
@gitlab/ or @gl/ | Cloned from GitLab | zpm load @gitlab/user/repo |
@bitbucket/ or @bb/ | Cloned from Bitbucket | zpm load @bitbucket/user/repo |
@git/ | Cloned from any Git URL via origin: tag | zpm load @git/custom,origin:https://git.example.com/repo.git |
@gist/ | Downloaded from GitHub Gist | zpm load @gist/username/gist_id |
@remote/ | Single file downloaded via curl | zpm load @remote/prompt,origin:https://example.com/prompt.zsh |
@dir/ / @link/ | Symlinked from a local directory | zpm load @dir/my-plugin,origin:$HOME/Projects/my-plugin |
@file/ | Symlinked from a single local file | zpm load @file/my-script,origin:$HOME/dotfiles/script.zsh |
@exec/ | Output generated by executing shell command | zpm load @exec/kubectl-completion,origin:"kubectl completion zsh",destination:completion |
@empty/ | Creates an empty directory (useful with hook:) | zpm load @empty/custom-tool,hook:"cargo install ..." |
@omz | Clones oh-my-zsh core repository | zpm load @omz |
@omz/plugin-name | Loads plugin from <omz-dir>/plugins/ | zpm load @omz/git |
@omz/theme/theme-name | Loads theme from <omz-dir>/themes/ | zpm load @omz/theme/robbyrussell |
@omz/lib/lib-name | Loads library from <omz-dir>/lib/ | zpm load @omz/lib/completion |
Important for Oh-My-Zsh: Load
@omzbefore loading individual OMZ plugins, themes, or libraries.
Plugin Tags
apply
Controls what components of the plugin are activated. Takes colon-separated values:
source— source the main plugin script file (enabled by default).path— add plugin'sbin/directory to$PATH(enabled by default ifbin/exists).fpath— add plugin'sfunctions/(or completions) directory to$fpath(enabled by default).
# Only add completions to $fpath without sourcing any script
zpm load zsh-users/zsh-completions,apply:fpath
# Only add bin/ executables to $PATH
zpm load some/cli-tool,apply:path
async
Defers plugin sourcing until after the primary prompt is rendered, significantly speeding up initial shell display.
zpm load zsh-users/zsh-autosuggestions,async
zpm load zdharma-continuum/fast-syntax-highlighting,async
destination
Specifies how downloaded files or generated outputs should be placed within the plugin directory:
plugin(default) — saved as<name>.zshand sourced.completion— saved intofunctions/_<basename>as an autoloadable completion file.bin— saved intobin/<basename>and automatically marked executable (chmod +x).
# Download a standalone CLI tool and make it executable in $PATH
zpm load @remote/git-standup,origin:https://raw.githubusercontent.com/kamranahmedse/git-standup/master/git-standup,destination:bin
# Generate completion dynamically on install
zpm load @exec/rustup-completion,origin:"rustup completions zsh",destination:completion
source
Specifies a custom script file to source if the main entry file cannot be detected automatically.
zpm load some/plugin,source:custom-init.zsh
path and fpath
Specifies custom directory locations within the plugin to add to $PATH or $fpath.
zpm load some/plugin,path:executables
zpm load some/plugin,fpath:completions
autoload
Declares functions to be autoloaded on demand (autoload -Uz).
zpm load some/plugin,autoload:func1:func2:func3
origin
Overrides the download or clone source URL / path / command.
zpm load @git/private-plugin,origin:git@github.com:company/private-plugin.git
zpm load @dir/local-theme,origin:$HOME/.dotfiles/themes/my-theme
hook
Runs a shell command in the plugin directory immediately after installation or upgrade.
zpm load junegunn/fzf,hook:"./install --bin"
zpm load romkatv/powerlevel10k,hook:"git submodule update --init --recursive"
Conditional Loading (if and if-not)
Load plugins conditionally based on the operating system or environment. Conditions are evaluated during cache generation on first run.
zpm if <condition> load <plugin>
zpm if-not <condition> load <plugin>
Available Conditions:
linux— Linux operating systemmacos— macOS (Darwin)bsd— BSD variants (FreeBSD, OpenBSD, NetBSD)openwrt— OpenWrt router environmenttermux— Android Termuxssh— Remote SSH session ($SSH_CONNECTION/$SSH_TTY)vte— Terminal emulator with VTE supportmsys— MSYS / Cygwin environment on Windowsvscode— Integrated terminal in VS Codeiterm— iTerm2 terminal emulator
Conditions can be chained:
# Only load on macOS and when not connected over SSH
zpm if macos if-not ssh load zpm-zsh/iterm
Cookbook & Common Examples
1. Oh-My-Zsh Plugins & Themes
# Step 1: Clone OMZ core
zpm load @omz
# Step 2: Load required libraries
zpm load \
@omz/lib/completion \
@omz/lib/history \
@omz/lib/key-bindings
# Step 3: Load OMZ plugins and theme
zpm load @omz/git @omz/extract
zpm load @omz/theme/robbyrussell
2. High-Performance Modern Setup (Async Plugins)
# Immediate sync loading
zpm load zpm-zsh/helpers
zpm load romkatv/powerlevel10k
# Async deferred loading
zpm load zsh-users/zsh-autosuggestions,async
zpm load zdharma-continuum/fast-syntax-highlighting,async
zpm load zsh-users/zsh-history-substring-search,async
3. Local Plugin & Single-File Development
# Symlink entire local repository
zpm load @dir/my-plugin,origin:$HOME/Projects/zsh-my-plugin
# Symlink a standalone snippet/script
zpm load @file/aliases,origin:$HOME/.dotfiles/zsh/aliases.zsh
4. Completions and Standalone Binaries
# Add completions from repo without sourcing anything
zpm load zsh-users/zsh-completions,apply:fpath
# Download single-file binary and expose to $PATH
zpm load @remote/git-quick-stats,origin:https://raw.githubusercontent.com/arzzen/git-quick-stats/master/git-quick-stats,destination:bin
Configuration & Environment Variables
| Variable | Default | Description |
|---|---|---|
_ZPM_PARALLEL_RUNNER | auto (parallel -> rush -> xargs) | Force runner for concurrent downloads (parallel, rush, xargs) |
CLICOLOR | 1 | Set CLICOLOR=0 to disable ANSI colors in terminal output |
DEBUG | (none) | Enable debug logging. Supports prefixes: DEBUG=zpm, DEBUG=zpm:init, DEBUG=zpm:install, DEBUG=zpm:upgrade |
GITHUB_MIRROR | https://github.com | Custom mirror URL for GitHub |
GITLAB_MIRROR | https://gitlab.com | Custom mirror URL for GitLab |
BITBUCKET_MIRROR | https://bitbucket.org | Custom mirror URL for Bitbucket |
ZSH_DATA_HOME | ${XDG_DATA_HOME:-$HOME/.local/share}/zsh | Root directory for installed plugins |
ZSH_CACHE_HOME | ${XDG_CACHE_HOME:-$HOME/.cache}/zsh | Root directory for zcompdump |
ZSH_TMP_DIR | /tmp/zsh-${UID:-user} | Directory for compiled cache, aggregated functions and bin files |
# Example: configuration before sourcing zpm.zsh
export _ZPM_PARALLEL_RUNNER="xargs"
export GITHUB_MIRROR="https://hub.fastgit.org"
Guide for Plugin Authors
If you are authoring a plugin, you can include a plugin-options.zsh file in the root of your repository. ZPM will automatically read this file before initializing the plugin:
# plugin-options.zsh
zpm_plugin_source=true # set to false if the plugin should not be sourced
zpm_plugin_async=false # set to true if the plugin should always load asynchronously
zpm_plugin_bin_path=true # add plugin's bin/ to $PATH
zpm_plugin_functions_path=true # add plugin's functions/ to $fpath
zpm_plugin_autoload="foo:bar" # functions to autoload automatically
Troubleshooting
Built-in Compatibility Workarounds
ZPM automatically detects and applies optimizations for popular plugins:
romkatv/powerlevel10k:POWERLEVEL9K_INSTALLATION_DIRis automatically configured in cache.zsh-syntax-highlighting&zsh-history-substring-search: Main entry files are resolved automatically.sindresorhus/pure: Prompt function paths are resolved automatically.
Clearing Cache and Updating ZPM
If you modify your .zshrc or encounter corrupted cache files:
# Quick cache refresh
zpm clean
# Updating ZPM itself
zpm upgrade @zpm
Developing process
Set
DEBUG=zpmto view verbose debug logs during plugin resolution and caching.
When contributing changes, add information about them to the changelog in the next section, including pull request and profile links.
TODO
- Create logo
- Improve readme
- Describe installation process
- Describe all tags, plugin types, and configuration options
- Improve completions
- Now
zpm load,zpm upgradeorzpm subcommandwill complete only one argument
- Now
Changelog
-
6.1
- Move zpm default location to
$XDG_DATA_HOME/zsh/plugins/@zpm - Prevent
@zpm-compilefrom compiling.gitrefs whensetopt GLOB_DOTSis enabled - Fix installation paths in README
- Add benchmarking comparison in README
- Stop exporting internal parameters into child shells
- Add
-fSLflag to let curl follow redirections
- Move zpm default location to
-
6.0
- Add workarouds for powerlevel10k, zsh-syntax-highlighting, zsh-history-substring-search, sindresorhus/pure
-
5.3
- Change plugin file path detector
-
5.2
- Remove old omz tag
-
5.1
- Change internal functions
-
5.0
- Removed
gen-pluginandgen-completiontags @omz-theme/and@omz-lib/changed to@omz/theme/and@omz/lib/
- Removed
-
4.2
-
4.1
- Added possibility to change parallel runner, e.g. GNU Parallel, Rush, Xargs
- Optimize plugin load
- Change some plugin urls
-
4.0
- Refactoring of internal logic
- Added new plugin types:
@gist,@remote
-
3.6
- Added new plugin types
@dirand@file @linknow is an alias for@dir- Fixed #35
- Added new plugin types
-
3.5
- Added new logo
- Added possibility to use mirrors for GitHub/Gitlab/Bitbucket. See issue
-
3.4
- Added GNU Parallel
-
3.3
- Added
origintag - Removed
autoload-alltag
- Added
-
3.2
- Fix plugin load order
- Use sched for background run
-
3.1
- Fix completions
- Add example for @omz
-
3.0
- Remove unused
@link - Remove
trcalls - Deprecate
type:tag - Internal changes for basename/name,hyperlink
- Add support for oh-my-zsh themes and libs
zpm load @omz-theme/theme-namezpm load @omz-lib/lib
- Upgrade from 2.x:
- Add
zpm load @omzif you use at least one oh-my-zsh plugin. - Replace
type:plugin-typewith@plugin-type/plugin/name
- Add
- Remove unused
-
2.3
- Improve README
- Remove suppot for
zsh_loaded_plugins - Add config for Markdownlint
-
2.2
- Add support for OpenWrt
- Improve oh-my-zsh-support @igetgames
- Support for calling plugin functions from command tags @igetgames
- Fix autoload option processing @igetgames
-
2.1
- Optimizations
- Now all content of
/functionsand/binwill be copied into single dir, in zpm cache dir - Change
zpmto@zpm - Remove unused vars
- Some vars will be loaded async
- Fixed colors
- Notes
- Now for update zpm need to run
zpm u @zpm
- Now for update zpm need to run
-
2.0
omz/prefix replaced by@omz/- Added plugin type
empty - Added
autoloadandautoload-alltags - Added
gen-pluginandgen-completiontags - Notes:
- Replace
omz/to@omz/in your.zshrc
- Replace