Documentation Index Fetch the complete documentation index at: https://mintlify.com/sxyazi/yazi/llms.txt
Use this file to discover all available pages before exploring further.
Learn how to create custom plugins to extend Yazi’s functionality using Lua.
Plugin Basics
Yazi plugins are written in Lua and follow a simple structure. Each plugin is a Lua module that returns a table with functions.
Minimal Plugin Example
Here’s the simplest possible plugin:
-- ~/.config/yazi/plugins/hello.lua
local M = {}
function M : entry ()
ya . notify {
title = "Hello Plugin" ,
content = "Hello from Yazi!" ,
timeout = 3 ,
level = "info"
}
end
return M
Call it from your keymap:
[[ manager . prepend_keymap ]]
on = [ "h" , "i" ]
run = "plugin hello"
desc = "Say hello"
Plugin Structure
Module Pattern
All plugins follow this pattern:
local M = {} -- Create module table
-- Add methods to module
function M : entry ()
-- Plugin logic here
end
return M -- Return module
The M table can contain:
Methods (functions)
State variables
Configuration options
Entry Points
Different plugin types have different entry points:
Plugin Type Entry Point Purpose Functional entry(job)Main execution function Previewer peek(job), seek(job)Preview generation Fetcher fetch(job)Metadata retrieval Spotter spot(job)Info panel display UI Component redraw()UI rendering
Step-by-Step Plugin Creation
Let’s create a plugin that counts files in the current directory.
Create the plugin file
Create ~/.config/yazi/plugins/filecount.lua: local M = {}
function M : entry ()
-- We'll add logic here
end
return M
Access current directory
Use the cx (context) global to access Yazi’s state: function M : entry ()
local cwd = cx . active . current . cwd
local files = cx . active . current . files
ya . notify {
title = "File Count" ,
content = string.format ( "%d files in %s" , # files , tostring ( cwd )),
timeout = 3
}
end
Add filtering logic
Count different file types: function M : entry ()
local files = cx . active . current . files
local dirs , regulars , hidden = 0 , 0 , 0
for _ , file in ipairs ( files ) do
if file . cha . is_dir then
dirs = dirs + 1
else
regulars = regulars + 1
end
if file . name : sub ( 1 , 1 ) == "." then
hidden = hidden + 1
end
end
ya . notify {
title = "File Count" ,
content = string.format (
"Total: %d \n Directories: %d \n Files: %d \n Hidden: %d" ,
# files , dirs , regulars , hidden
),
timeout = 5
}
end
Bind to a key
Add to keymap.toml: [[ manager . prepend_keymap ]]
on = [ "c" , "c" ]
run = "plugin filecount"
desc = "Count files in directory"
Accessing Yazi State
Yazi provides global objects to access its state:
cx - Context
The main state object:
-- Current tab
local current = cx . active . current
local cwd = current . cwd -- Current directory
local files = current . files -- Files in current dir
local hovered = current . hovered -- Currently hovered file
-- Selection
local selected = cx . active . selected -- Selected files
-- Yanked files (copy/cut)
local yanked = cx . yanked
local is_cut = cx . yanked . is_cut
-- Tab info
local mode = cx . active . mode -- Select/unset/normal mode
rt - Runtime
Configuration and runtime settings:
-- Preview settings
local max_width = rt . preview . max_width
local image_quality = rt . preview . image_quality
-- Manager settings
local show_hidden = rt . mgr . show_hidden
local show_symlink = rt . mgr . show_symlink
th - Theme
Access theme colors and styles:
-- Manager theme
local cwd_style = th . mgr . cwd
local hovered_style = th . mgr . hovered
-- Status bar theme
local status_style = th . status . overall
Using the Lua API
Yazi provides a rich API through the ya global:
Notifications
ya . notify {
title = "Title" ,
content = "Message" ,
timeout = 3 , -- Seconds
level = "info" -- "info", "warn", "error"
}
local value , event = ya . input {
title = "Enter name:" ,
pos = { "center" , x = 50 , y = 50 },
obscure = false -- true for password input
}
if event == 1 then
-- User confirmed
ya . notify { content = "You entered: " .. value }
end
File Operations
-- Read directory
local entries = fs . read_dir ( path , { limit = 100 })
-- Check file attributes
local cha = fs . cha ( path )
if cha and cha . is_dir then
-- It's a directory
end
-- Create/remove
fs . write ( path , "content" )
fs . remove ( "file" , path )
fs . remove ( "dir" , path )
Running Commands
-- Run command and get output
local output , err = Command ( "ls" )
: arg ({ "-la" , "/tmp" })
: stdout ( Command . PIPED )
: output ()
if output then
ya . notify { content = output . stdout }
end
-- Spawn async process
local child , err = Command ( "ffmpeg" )
: arg ({ "-i" , "input.mp4" })
: stdout ( Command . PIPED )
: stderr ( Command . PIPED )
: spawn ()
if child then
local output = child : wait_with_output ()
end
Emitting Events
-- Navigate to directory
ya . emit ( "cd" , { "/path/to/dir" , raw = true })
-- Reveal file
ya . emit ( "reveal" , { Url ( "/path/to/file" ) })
-- Open file
ya . emit ( "open" , {})
-- Toggle selection
ya . emit ( "toggle" , {})
Plugin Configuration
Setup Function
Plugins can have a setup() function for initialization:
local M = {}
function M : setup ( opts )
opts = opts or {}
self . auto_save = opts . auto_save or false
self . interval = opts . interval or 60
if opts . on_init then
opts . on_init ()
end
end
function M : entry ()
if self . auto_save then
-- Use configured option
end
end
return M
Call setup in init.lua:
require ( "myplugin" ): setup {
auto_save = true ,
interval = 30
}
Async Programming
For long-running operations, use async:
function M : entry ()
ya . async ( function ()
-- This runs in background
local result = Command ( "slow-command" ): output ()
-- Update UI from async
ya . sync ( function ()
ya . notify { content = "Done!" }
end )
end )
end
Error Handling
function M : entry ()
local ok , result = pcall ( function ()
-- Code that might fail
return Command ( "risky-command" ): output ()
end )
if not ok then
ya . notify {
title = "Error" ,
content = tostring ( result ),
level = "error"
}
end
end
Best Practices
Always declare variables with local to avoid polluting the global namespace: -- Good
local count = 0
-- Bad
count = 0 -- Global variable
Always validate data before using it: local hovered = cx . active . current . hovered
if not hovered then
return
end
-- Safe to use hovered
local name = hovered . name
Use async for slow operations
Don’t block the UI thread: -- Good
ya . async ( function ()
local result = Command ( "slow-cmd" ): output ()
end )
-- Bad - blocks UI
local result = Command ( "slow-cmd" ): output ()
Always notify users of success or failure: local output , err = Command ( "tool" ): output ()
if not output then
ya . notify {
title = "Error" ,
content = tostring ( err ),
level = "error"
}
end
Debugging
Print to stderr
ya . err ( "Debug value: " .. tostring ( value ))
Check Yazi logs
Logs are written to:
Linux/macOS: ~/.local/state/yazi/yazi.log
Windows: %APPDATA%\yazi\state\yazi.log
Next Steps
UI Plugins Customize Yazi’s interface
Functional Plugins Add new commands
Previewers Create file previewers
Fetchers Build metadata fetchers