Conventions & Best Practices¶
This page defines the terminology used across the EasyBar Lua docs and the authoring practices that keep widgets predictable and easy to debug.
Widget¶
A widget is one Lua entrypoint executed by EasyBar. It may be a manually managed .lua file below your configured widgets_dir or the declared entrypoint of an installed Widget Store package.
The entrypoint can create one node or many nodes. It can also keep local state, run commands, subscribe to events, and update its own nodes over time.
Module¶
A module is reusable Lua code loaded with standard require(...) calls. Put reusable manual modules below shared/; installable packages expose reusable Lua only through declared exports, which EasyBar activates below the managed shared/ directory. Modules below shared/ are not started as widgets.
See Reusable Modules.
Node¶
A node is one renderable unit in the EasyBar runtime.
Examples:
- an
item - a
group - a
row - a
column - a
popup - a
slider
Node kinds are listed in Node Kinds.
Handle¶
A handle is the object returned by easybar.add(...).
You keep the handle in a local variable and call methods on it later:
node:set(...)node:get()node:remove()node:subscribe(...)
See Functions.
Built-in¶
A built-in is a native widget configured in config.toml under [builtins.*].
Built-ins are not Lua widgets, even when they resemble the same kind of visual element.
See Choose Built-ins, Widget Store, Or Lua.
Group¶
A group is a node kind used to give multiple child nodes one shared container.
Use it when several items should share:
- one background
- one border
- one padding box
- one popup owner
- one overall layout block
See Grouping.
Popup¶
A popup is extra content attached to a parent node.
Popup child nodes target a position of popup.<parent-id>. The parent node controls whether the popup is visible through popup.drawing.
See Popups.
Event¶
An event is a runtime signal that can trigger widget logic.
Examples:
- app switch
- space change
- volume change
- mouse enter
- mouse click
- slider preview
Events are exposed through easybar.events.*. Event names and payloads are documented in Events.
Agent¶
An agent is a helper process used for permission-sensitive or platform-specific data collection, such as calendar or network state.
Agents are configured in config.toml and feed data back into EasyBar events and native widgets.
See Agents.
Runtime¶
The runtime is the host machinery that loads widget files, creates nodes, dispatches events, and applies updates between the Lua side and the native app.
See Runtime Control.
Mental model¶
The shortest accurate mental model is:
- a widget file creates nodes
- EasyBar returns handles
- your code stores those handles
- events and timers trigger updates
set(...)mutates current node state
If you are just getting started, continue with First Widget.
Authoring practices¶
Use stable IDs¶
Node IDs should be stable across reloads:
easybar.add(easybar.kind.item, "brew_outdated", {
position = "right",
})
Avoid dynamic IDs unless you are intentionally creating a dynamic list.
Store handles¶
Always keep the handle returned by easybar.add(...) when the node needs updates or subscriptions.
local clock = easybar.add(easybar.kind.item, "clock", {
label = os.date("%H:%M"),
})
Prefer state-driven rendering¶
Keep state in Lua variables and render from that state.
local count = 0
local widget
local function render()
widget:set({
label = tostring(count),
})
end
Prefer groups for composite widgets¶
Use group when multiple child nodes belong together visually.
Use child subscriptions when only specific parts should be clickable.
Prefer theme values for shared styling¶
Use easybar.theme.colors.<token> when you want one resolved hex color in Lua.
Use easybar.theme.ref.<token> when a node color field should stay coupled to the active theme.
Use events before polling¶
Prefer event subscriptions for real runtime events.
Use intervals only for polling external state.
Avoid blocking the runtime¶
Prefer easybar.spawn_async(...) for ordinary executable invocations and easybar.exec_async(...) only when shell syntax is required. Use easybar.after(...) for delays instead of launching sleep.
Avoid expensive synchronous work in mouse handlers, interval callbacks, and frequent events.
Keep side effects inside handlers¶
Avoid doing too much work at file load time.
Good places for side effects:
on_intervalnode:subscribe(...)handlers- explicit refresh functions
Format with StyLua¶
Use StyLua to keep widget files consistently formatted:
brew install stylua
stylua ~/.config/easybar/widgets
Check formatting without changing files:
stylua --check ~/.config/easybar/widgets