sbar / docsSource ↗
sbar / Documentation

Text templates

Customise item labels with provider values and conditional sections.

Set an item’s text to replace its displayed label. Use a plain string for fixed text, or insert provider values with {{field}}. Omit text or set it to null to keep the existing presentation.

{
  "id": "cpu",
  "type": "cpu",
  "text": "CPU {{percentage}}%"
}

An empty string hides the text without hiding the item or removing its padding. Item symbols, provider symbols, and native application icons remain visible. Throughput direction icons require a transfers loop with {{symbol}}. Groups, dividers, and spacers reject text. For a group, set templates on its children instead.

Values and sections

SyntaxResult
{{field}}Insert a value; a missing value becomes an empty string.
{{#field}}...{{/field}}Include the contents when the value is present.
{{^field}}...{{/field}}Include the contents when the value is missing, empty, or false.

Spaces around a tag are allowed, for example {{ percentage }}. Templates insert values as plain text, without HTML escaping or evaluating tags inside those values. An empty value or the literal value false counts as absent. Numeric zero counts as present, so a section around unreadCount also matches zero unread messages. Sections can nest up to eight levels.

Presence sections test whether a value exists. Equality sections compare a value with an expected string.

Missing readings

Use available to avoid displaying an incomplete value while a reading is missing:

{
  "id": "memory",
  "type": "memory",
  "text": "{{#available}}RAM {{used}} / {{total}}{{/available}}{{^available}}RAM unavailable{{/available}}"
}

Optional metadata

Put a separator inside a section so it appears only with the corresponding metadata:

{
  "id": "media",
  "type": "media",
  "text": "{{#artist}}{{artist}}: {{/artist}}{{title}}{{^title}}Nothing playing{{/title}}"
}

Provider hide rules still apply. For example, media.hideWhenNotPlaying: true hides this item during paused, stopped, or unavailable playback even if the template produces text.

Keep the default label as a fallback

value contains the provider’s default label, including error messages:

{
  "id": "mail",
  "type": "mail",
  "text": "{{#available}}Inbox: {{unreadCount}}{{/available}}{{^available}}{{value}}{{/available}}"
}

State-specific labels

Equality sections select a label for a particular state. Close them with the field name alone:

{
  "id": "vpn",
  "type": "vpn",
  "text": "{{#status=connected}}Secure{{/status}}{{^status=connected}}{{value}}{{/status}}"
}

{{#status=connected}} includes its contents when the status equals connected. {{^status=connected}} includes its contents when the status differs or is missing. The example keeps the default provider label for every other state. Sections can nest, including conditions on the same field.

Comparisons use case-sensitive strings. Whitespace around the field, =, and expected value is ignored. Expected values are unquoted text, so {{#title=A song}}...{{/title}} matches that exact title. Numeric values compare their formatted strings; there are no arithmetic or ordering comparisons. Empty expected values are rejected; use {{^field}} to handle empty or missing fields. Boolean comparisons accept true and false. Booleans render as true or false.

Configuration validation checks the allowed status values for each provider. For example, {{#status=conected}} fails configuration validation rather than silently hiding text.

See the provider references below for accepted states and field meanings.

Workspace fields and lists

See Spaces, AeroSpace, and Yabai for workspace fields, loop examples, scope, and entry styling.

VPN services and Bluetooth devices

See VPN services and Bluetooth devices for collection fields, examples, and aggregate state behaviour.

Throughput fields and symbols

See throughput templates for rate fields, direction conditions, and icon examples.

Collection rules

Collections cannot nest, be compared, or be inserted as scalar values. Each provider accepts only its own collection. {{#separator}}...{{/separator}} is valid only inside a collection, with no inverse, comparison, or scalar form. {{symbol}} is a shared value tag, valid inside or outside a collection. It cannot be used as a section or comparison.

Separators appear only between entries that render content. Entries that produce only a separator are omitted. Whitespace and symbol tags count as content, so keep literal spaces inside the condition when hiding an entry. Write spaces explicitly; templates add no spacing between runs.

first, last, index, and total still describe the source list. An inverse collection section tests whether that source list is empty, not whether conditions hid every entry. Multiple loops may appear beside one another; each renders independently.

Filter a list without stray separators

This shows visible workspaces, with separators only between entries that render:

{
  "id": "spaces",
  "type": "spaces",
  "text": "{{#workspaces}}{{#visible}}{{name}}{{/visible}}{{#separator}} · {{/separator}}{{/workspaces}}{{^workspaces}}{{value}}{{/workspaces}}"
}

Use the separator section for punctuation after filtering. A condition such as {{^last}}, {{/last}} uses the source position and can leave a trailing comma when the final source entry is hidden.

Provider fields

Every item that accepts text supports id, value, and symbol. id is the item’s configuration ID. value is the provider’s default label. symbol requests its native icon or the item’s configured symbol. Use the template to choose individual fields or text: "" to hide the label.

Provider pages define their fields, accepted states, and examples:

Static text and popup items have no additional fields.

Fields follow the item’s media source, audio endpoint, disk path, network interface, and CPU/throughput smoothing settings. They also use its refresh snapshot. Editing the template changes the presentation of a held reading without refreshing it.

Symbols, visibility, and accessibility

{{symbol}} requests the provider’s native symbol or the item’s explicit symbol. It displays the icon itself. The icon follows symbolPosition, regardless of where the tag appears. Repeated tags retain one aggregate icon. Throughput loops can render one direction icon per run.

Without a symbol tag, templates retain the provider’s normal icon behaviour. Once a template contains a symbol tag anywhere, the icon appears only if a branch containing that tag renders. Throughput direction icons always require a tag inside transfers when using an explicit template.

Show an icon conditionally

This shows the battery icon only while charging, with a fallback label when no percentage is available:

{
  "id": "battery",
  "type": "battery",
  "text": "{{#charging}}{{symbol}}{{/charging}}{{#available}}{{percentage}}%{{/available}}{{^available}}{{value}}{{/available}}",
  "symbolPosition": "right"
}

At 50% while charging, the text is 50% with the icon on the right. On battery power, the same text has no icon. Removing the symbol tag entirely restores the normal battery icon behaviour.

Provider showSymbol: false still hides symbols. A symbol tag does not create an icon for a provider that has none. Workspace, VPN, and Bluetooth loops retain one aggregate icon, even if several entries render the tag. Use provider state conditions around {{symbol}}; {{#symbol}} and {{#symbol=...}} are invalid.

Conditional application icons

See front application templates for conditional native icons and the showIcon option.

Icon-only templates

Use {{symbol}} alone to request an icon without text:

{
  "id": "volume",
  "type": "volume",
  "text": "{{symbol}}"
}

An empty template also retains ordinary provider icons, but hides throughput direction icons. For those, use the directional icon-only example.

Workspace loops retain each entry’s colour and emphasis. Throughput transfers loops retain direction icons when they include {{symbol}}; scalar rate fields render text alone. An item-level symbol supplies one shared icon. See throughput visibility for symbol overrides and empty templates.

Item symbols, native application icons, item colours, and provider hide rules still apply. Native providers retain their existing accessibility descriptions. Command and plugin descriptions also retain their original presentation. Static, popup, application, and clock labels use the rendered text.

Validation and runtime changes

Unknown fields, unmatched tags, unclosed sections, and nesting beyond eight levels fail sbar validate. Collections used as values, nested collections, separator tags outside a collection, and symbol sections also fail validation. Fields must belong to the item’s type, so {{artist}} is invalid on a CPU item. A failed reload retains the previous valid configuration.

Edit text in the configuration file and let it reload. sbar set cannot change it. Templates affect displayed labels only; CLI values and subscription events keep their existing output. A text field returned by a command or plugin is result data, not a template.

Validate and try a template

Save a complete configuration and validate it before running the bar:

sbar validate --config "$PWD/config.json"
sbar start --config "$PWD/config.json"

The JSON item examples on this page belong inside items.left, items.center, or items.right. For example:

{
  "schemaVersion": 1,
  "bar": {},
  "items": {
    "right": [
      {
        "id": "cpu",
        "type": "cpu",
        "text": "{{#available}}{{symbol}}CPU {{percentage}}%{{/available}}{{^available}}{{value}}{{/available}}"
      }
    ]
  }
}

Check each condition with matching and non-matching values on the Mac running the bar. Connect and disconnect a VPN service, switch Spaces, or change the foreground application to exercise the relevant example. For throughput, check active transfers, zero rates, and unavailable readings. Zero remains an available reading.

With manual refresh, the label and conditional icon use the held snapshot. Change the underlying state, then run sbar trigger <item-id> to capture it. Editing text alone renders the held values again. See refresh policies for interval and event behaviour.

Current limits

There are no custom number formatters, arbitrary command/plugin JSON field lookups, or escapes for literal {{.

See command templates, plugin templates, and date formatting for result and formatting behaviour.

sbar documentation Built with Hugo · Reference reviewed 12 Sep 2026