Features

Conditions, lists and templates

A widget can change by itself, without the app running: switch to prepared layouts at set times, show text in the device language, and fetch its own data. All three live in the same WidgetConfig, and every platform resolves them right before drawing (src/resolve.rs, with Swift, Kotlin and TypeScript ports checked against the same fixtures in tests/fixtures/resolve/).

Conditions (when)

when on any element inside children (or on a family root) keeps the element only when the condition holds; it is stripped before drawing. A condition reads one value by path (the same paths as placeholders: data.…, t.…, locale, instance, loop variables):

{ "type": "text", "content": "Rain soon", "when": { "path": "data.rain", "gt": 0.5 } }
KeyHolds when
eq / neequal / not equal (JSON equality, numbers by value; a missing value fails both)
gt / gte / lt / lteboth sides are numbers and compare so
inthe value equals one of the array items
existstrue: the value is present; false: it is missing or null
only paththe value is truthy (not false, 0, "", [], null, missing)
all / any / notcombine other conditions: { "any": [ {…}, {…} ] }

Several operators in one object must all hold. A malformed condition is false.

Lists from data (repeat)

repeat turns an array into elements. It is replaced in its parent by one copy of template per item, so it works inside any stack, grid or list:

{
  "data": { "days": [{ "d": "Mon", "hi": 21 }, { "d": "Tue", "hi": 18 }] },
  "small": {
    "type": "vstack",
    "children": [
      { "type": "repeat", "each": "data.days", "as": "day", "limit": 5,
        "template": { "type": "text", "content": "{{index}}. {{day.d}} {{day.hi}}°" } }
    ]
  }
}

as names the loop variable (default item); {{index}} counts from 0. At most 50 copies; limit lowers that. A path that is not an array renders nothing. A loop variable hides a built-in root of the same name (as: "t" hides {{t.…}} inside the template). A repeat used as a family root is wrapped in a vstack.

Reusable parts (templates, use)

{
  "templates": {
    "row": { "type": "label", "systemName": "{{arg.icon}}", "text": "{{arg.text}}" }
  },
  "small": {
    "type": "vstack",
    "children": [
      { "type": "use", "template": "row", "args": { "icon": "sun.max", "text": "{{data.temp}}°" } },
      { "type": "use", "template": "row", "args": { "icon": "drop", "text": "{{data.hum}}%" }, "color": "#60a5fa" }
    ]
  }
}

Arguments are read as {{arg.name}} (or with bind). An argument that is exactly one placeholder, such as "{{data.days}}", keeps its JSON type, so a template can repeat over it. Other keys on the use element (color, padding, …) override the template's root. An unknown template renders nothing; templates nest up to 8 levels.

Localized strings (strings)

{{t.key}} in any string takes the value from the table for the device locale: exact tag (pt-BR, pt_BR too; script and extension subtags are ignored, so zh-Hant-TW-u-… matches zh-TW), then the same language, then defaultLocale, then en, then the first table. A key missing there falls back to the default table, then shows the key.

{
  "defaultLocale": "en",
  "strings": {
    "en": { "title": "Weather", "humidity": "Humidity" },
    "ru": { "title": "Погода", "humidity": "Влажность" }
  },
  "small": { "type": "text", "content": "{{t.title}}" }
}

{{locale}} is the device locale itself. On the Windows Widgets Board the host reads it from LC_ALL / LC_MESSAGES / LANG, which are often unset there — then the defaultLocale table is used.

Newer configs on older apps (requires)

Renderers understand contract version 3 (everything on this page; version 2 had no state / set / transition). A config that needs a newer one says so, and an older app shows fallback instead of a half-understood layout:

{
  "requires": { "resolve": 3 },
  "fallback": { "small": { "type": "text", "content": "Update the app" } },
  "small": { "…": "uses a future feature" }
}

Without fallback the widget shows "Update the app to show this widget". strings and defaultLocale still apply to the fallback.