Oh My Zsh's $ZSH/lib Loads Before Plugins—Here's Why That Matters
Oh My Zsh sources $ZSH/lib/ before plugins and themes, making lib functions globally available but risking silent overrides.
25 Mar 2026, 17:58 UTC

The Problem: A Function Works Until It Doesn't
You add a helper to your zsh configuration, call it from a plugin, and it works—until it doesn't. Another plugin defines a function with the same name, or your helper runs too late to affect early-loading plugins. The issue is often not the plugins=(...) array. It is the $ZSH/lib/ directory, which Oh My Zsh sources before any plugin or theme code runs.
What Loads First: The Lib Directory
During startup, Oh My Zsh executes every *.zsh file in $ZSH/lib/ in alphabetical order. This happens before the plugin array is processed and before the theme is sourced. Default files include completion.zsh, git.zsh, history.zsh, prompt.zsh, and others.
Because lib files run first, functions such as take or git_prompt_info are available to every plugin and theme without explicit source calls. That also means a custom lib file can override a built-in function before the original code ever runs.
Worked Example: A Shared Project-Root Helper
Suppose you want a projroot function that finds the nearest directory containing .git, Cargo.toml, or package.json, and you need it available to early-loading plugins such as git.
Step 1: Create the file with a numeric prefix so it loads before built-in lib files.
cat > "$ZSH/lib/00-projroot.zsh" <<'EOF'
# Find the nearest project root directory
projroot() {
local dir="$PWD"
while [[ "$dir" != "/" ]]; do
if [[ -e "$dir/.git" || -e "$dir/Cargo.toml" || -e "$dir/package.json" ]]; then
echo "$dir"
return 0
fi
dir="${dir:h}"
done
return 1
}
EOF
Where to run: In an interactive zsh session. You need write permission to $ZSH/lib/, typically owned by your user.
Step 2: Reload Oh My Zsh.
omz reload
Step 3: Verify the function exists without sourcing anything else.
type projroot
Step 4: Test from a nested directory inside a repository.
cd ~/myproject/src/deep/nested
projroot
The function is available to plugins because 00-projroot.zsh ran before the plugin array was processed.
Trade-Off: Global Namespace Pollution
Every function defined in $ZSH/lib/ enters the global shell namespace. If you define git_prompt_info in a custom lib file, you override the built-in version used by themes. If two lib files define the same function, the file that sorts later wins silently.
Mitigation:
- Prefix custom functions with a unique identifier, such as
my_projroot. - Avoid overriding default lib functions unless replacement is intentional.
- Keep plugin-specific logic in its own plugin rather than in
$ZSH/lib/.
Verify What Is Actually Loading
List the directory to confirm automatic sourcing:
ls -la "$ZSH/lib/"
To see load order, add temporary echo statements to lib files and start a new shell. To confirm a function originated in lib, use type function_name after omz reload.
Closing
Treat $ZSH/lib/ as a foundation layer, not a snippet dump. Add only utilities that genuinely need to run before plugins, name them defensively, and respect alphabetical load order. When in doubt, put the code in a plugin instead.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.