Skip to content

custom ​

User-defined module that runs a shell command and renders the output in the bar.

Full walkthrough with examples in docs/guide/custom-modules.md.

Add it to your layout with custom-<id>:

yaml
bar:
  layout:
    - monitor: "*"
      right: [custom-<id>]

General ​

FieldTypeDefaultDescription
idstringrequiredUnique identifier for this module.
commandunknownnullShell command to execute.
modeExecutionMode"poll"Execution mode for the command.
interval-msu645000Polling interval in milliseconds.
formatstring"{{ output }}"Format string for the label using Jinja2 template syntax.
tooltip-formatunknownnullFormat string for the tooltip (hover text).
hide-if-emptyboolfalseHide module when output is empty, "0", or "false".
class-formatunknownnullFormat string for dynamic CSS classes.
label-showbooltrueDisplay text label.
label-max-lengthu320Maximum label length in characters before truncation.
border-showboolfalseDisplay border around button.
color-mapunknownnullMap of per-state color overrides keyed by the alt field value.
on-actionunknownnullShell command to run after any click/scroll action completes.
More about id

Referenced in bar layouts as custom-<id>. Must be unique across all custom module definitions.

Example ​

yaml
modules:
  custom:
    - id: gpu-temp

# Reference in layout:
# layout: [custom-gpu-temp, clock]
More about command

The command runs via sh -c and should output to stdout. Stderr is discarded. Commands have a 30-second timeout.

Output Parsing ​

  • If output starts with { or [: parsed as JSON
  • Otherwise: treated as plain text

Behavior by Mode ​

  • poll: Executed every interval-ms milliseconds
  • watch: Spawned once, each stdout line triggers a display update. Restarts are controlled by restart-policy.
More about mode
ModeBehavior
pollRun command every interval-ms (default)
watchSpawn long-running process, update on each stdout line

Use poll for commands that return current state and exit. Use watch for commands that stream updates (e.g., pactl subscribe).

More about interval-ms

Only applies to poll mode. Ignored in watch mode.

Set to 0 for manual polling mode: no timer is started. In manual mode, the command still runs once at startup.

More about format

Variables ​

  • {{ output }} - Raw command output
  • {{ field }} - JSON field access
  • {{ nested.field }} - Nested field access
  • {{ items.0 }} - Array index access

Filters ​

  • {{ val | default('fallback') }} - Fallback for missing values
  • {{ "%02d" | format(val) }} - Zero-padding
  • {{ val | upper }}, | lower, | trim - String transforms

Examples ​

  • "{{ output }}°C" - Plain text: "72°C"
  • "{{ percentage }}%" - JSON field: "75%"
  • "{{ data.temp }}°C" - Nested: "22°C"

If JSON output contains a text field, it overrides this format.

More about tooltip-format

Supports the same Jinja2 syntax as format. If not set, no tooltip is shown. If JSON output contains a tooltip field, it overrides this format.

Example ​

yaml
format: "{{ percentage }}%"
tooltip-format: "Volume: {{ percentage }}% on {{ device }}"
More about hide-if-empty

When enabled, the module (including its gap in the bar layout) is completely hidden if the output indicates an empty/disabled state.

More about class-format

Supports the same Jinja2 syntax as format. The formatted result is split on whitespace and each word is added as a CSS class.

Combined with the class field from JSON output (if present).

Example ​

yaml
class-format: "volume-{{ alt }}"
# Output: {"alt": "muted"} → adds class "volume-muted"
More about label-max-length

When exceeded, label is truncated with ellipsis. Set to 0 to disable.

More about color-map

Mirrors icon-map: the alt value from JSON output is looked up in this map and the colors set for that state override the module's static colors, letting a single widget cycle its colors alongside its icon as its state changes. Use "default" as a fallback key. Any color left unset for the matched state falls back to the module's static color of the same name.

Example ​

yaml
icon-map: {muted: ld-volume-x-symbolic}
color-map:
  muted: {icon-color: red}
  default: {icon-color: green}

# Output: {"percentage": 50, "alt": "muted"}
# Result: muted icon rendered in red
More about on-action

Executes after the action handler finishes, and its output updates the display immediately. Useful for reflecting state changes without waiting for the next poll interval.

Example ​

yaml
# Volume control with immediate feedback
scroll-up: pactl set-sink-volume @DEFAULT_SINK@ +5%
scroll-down: pactl set-sink-volume @DEFAULT_SINK@ -5%
on-action: |
  vol=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -oP '\d+(?=%)' | head -1)
  echo "{\"percentage\": $vol}"

Colors ​

FieldTypeDefaultDescription
icon-colorColorValue"auto"Icon foreground color.
icon-bg-colorColorValue"auto"Icon container background color.
label-colorColorValue"auto"Label text color.
button-bg-colorColorValue"bg-surface-elevated"Button background color.
border-colorColorValue"auto"Border color.

Click actions ​

FieldTypeDefaultDescription
left-clickClickAction""Action performed on left click.
right-clickClickAction""Action performed on right click.
middle-clickClickAction""Action performed on middle click.
scroll-upClickAction""Action performed on scroll up.
scroll-downClickAction""Action performed on scroll down.
More about left-click

Supports shell commands and dropdown toggles:

  • "pavucontrol" — runs a shell command
  • "dropdown:audio" — toggles the named dropdown panel
  • "" — no action (default)

If on-action is set, it runs after shell commands complete.

More about right-click

Supports shell commands and dropdown toggles (see left-click). If on-action is set, it runs after shell commands complete.

More about middle-click

Supports shell commands and dropdown toggles (see left-click). If on-action is set, it runs after shell commands complete.

More about scroll-up

Supports shell commands and dropdown toggles (see left-click). Scroll events are debounced (50ms) to coalesce rapid scrolls. If on-action is set, it runs after shell commands complete.

More about scroll-down

Supports shell commands and dropdown toggles (see left-click). Scroll events are debounced (50ms) to coalesce rapid scrolls. If on-action is set, it runs after shell commands complete.

Icons ​

FieldTypeDefaultDescription
icon-namestring""Static symbolic icon name.
icon-namesunknownnullArray of icon names indexed by percentage (0-100).
icon-mapunknownnullMap of icon names keyed by the alt field value.
icon-showbooltrueDisplay module icon.
More about icon-name

Used when icon-names and icon-map don't provide a match. Should be a symbolic icon name from the icon theme (e.g., "ld-gpu-symbolic").

Example ​

yaml
icon-name: ld-thermometer-symbolic
More about icon-names

Requires JSON output with a percentage field (0-100). The array is divided evenly across the percentage range.

Resolution ​

For N icons, icon at index floor(percentage * N / 101) is selected:

  • 4 icons: 0-24% → [0], 25-49% → [1], 50-74% → [2], 75-100% → [3]
  • 5 icons: 0-19% → [0], 20-39% → [1], 40-59% → [2], 60-79% → [3], 80-100% → [4]

Example ​

yaml
icon-names:
  - ld-battery-symbolic
  - ld-battery-low-symbolic
  - ld-battery-medium-symbolic
  - ld-battery-full-symbolic
More about icon-map

Requires JSON output with an alt field. The alt value is looked up in this map. Use "default" as a fallback key.

Priority: icon-map[alt] takes precedence over icon-names[percentage], allowing state-specific icons to override percentage-based icons.

Example ​

yaml
# Volume with muted state override
icon-names: [vol-0, vol-33, vol-66, vol-100]
icon-map: {muted: ld-volume-x-symbolic}

# Output: {"percentage": 50, "alt": "muted"}
# Result: Uses "ld-volume-x-symbolic" (alt match beats percentage)

# Output: {"percentage": 50}
# Result: Uses "vol-33" (percentage-based, no alt)

Restart ​

FieldTypeDefaultDescription
restart-policyRestartPolicy"never"Restart policy for watch mode.
restart-interval-msRestartDelay1000Base restart delay in milliseconds for watch mode.
More about restart-policy

Only applies to watch mode. Ignored in poll mode.

PolicyBehavior
neverDo not restart after exit
on-exitRestart after any exit
on-failureRestart only after non-zero/signal exit
More about restart-interval-ms

Only applies to watch mode. Ignored in poll mode.

Used when restart-policy is on-exit or on-failure. Delay increases exponentially on rapid failures, capped at 30 seconds.

Default configuration ​

Required fields (must be set in your config): id.

yaml
modules:
  custom:
    - mode: poll
      interval-ms: 5000
      restart-policy: never
      restart-interval-ms: 1000
      format: '{{ output }}'
      hide-if-empty: false
      icon-name: ""
      icon-show: true
      icon-color: auto
      icon-bg-color: auto
      label-show: true
      label-color: auto
      label-max-length: 0
      button-bg-color: bg-surface-elevated
      border-show: false
      border-color: auto
      left-click: ""
      right-click: ""
      middle-click: ""
      scroll-up: ""
      scroll-down: ""

Released under the MIT License.