JS reference
Every function and type tauri-plugin-widgets-api exports, generated from the JSDoc in
guest-js/api/. For how they fit together see the JavaScript API guide.
Widget IR types (WidgetConfig, WidgetElement, …) are generated from Rust into
guest-js/generated/widget-types.ts; they are documented per element on the Elements pages and in the JSON Schema.
| Function | Description |
|---|---|
setItems() | Store a key-value pair in the widget data store. |
getItems() | Read a previously stored value from the widget data store. |
reloadAllTimelines() | Reload all registered widget timelines. |
reloadTimelines() | Reload timelines for a specific widget kind or class. |
setRegisterWidget() | Register widget provider class names or kind strings. |
requestWidget() | Request the OS to pin (add) a widget to the home screen. |
createWidgetWindow() | Create a frameless, transparent desktop widget window. |
closeWidgetWindow() | Close a previously created desktop widget window. |
setWidgetConfig() | Send a declarative UI configuration to native widgets. |
setWidgetConfigFromPath() | Read a WidgetConfig JSON file from disk and push it like setWidgetConfig. |
getWidgetConfig() | Read the current widget UI configuration from the data store. |
reportReceipt() | Report that a renderer painted a config (desktop widget.html / tests). Native widgets write receipts themselves; this is for the host webview path. |
getWidgetDiagnostics() | Live widget instances that recently rendered for group. |
getWidgetTrace() | Host delivery journal + receipt history for group. Active in debug builds or when WIDGET_DEBUG=1. |
flushWidgetTrace() | Force-flush the in-memory journal to disk (desktop). |
widgetAction() | Trigger a widget-action event via the Rust plugin. |
onWidgetAction() | Listen for widget-action events emitted by widget buttons. |
pollPendingWidgetActions() | Poll pending widget actions queued by native side (Android fallback path). |
startWidgetUpdater() | Start a periodic widget updater that calls builder on a fixed interval, sends the returned config to the native widget, and optionally reloads widget timelines. |
getActiveWidgets() | List placed widgets. Exact on Android (AppWidgetManager) and iOS (WidgetCenter); on desktop: open widget windows plus placements that rendered in the last 48 h (macOS WidgetKit, Windows Widgets Board). |
setWidgetData() | Values for {{data.path}} placeholders, without resending the layout. Pass null to clear. Layers: config.data < this < the widget's own source fetch. |
setWidgetState() | Change a widget's local state ({{state.…}}, config state defaults) the way a set tap does: "$toggle", "$inc", "$dec", null removes a key (the default shows again), other values are stored. patch: null clears the stored state. Resolves with the merged state; the app also gets widget-state-changed {widgetId, instance, state}. |
getWidgetState() | A widget's merged local state: config state defaults, then what taps and setWidgetState stored (the placement's own over the widget's). |
setWidgetInstances() | Choices the user picks from per placement. iOS / macOS: the Edit Widget sheet of a widget built with TauriInstanceWidget / TauriInstanceProvider. The chosen id is the placement's instance. |
registerActivityTemplate() | Store a live activity layout on the device (shared store / App Group) under name. layout takes the ActivityContent layout fields (layout, lockScreen, compactLeading, islandCenter, title, …) with {{data.…}}, when, repeat; activities started with template: name then send only data. null removes it. |
startActivity() | Start a live activity, or update it when id already runs. |
updateActivity() | Replace the content of a running activity. |
endActivity() | End an activity; dismissAfterMs keeps the last state visible that long (iOS, desktop). |
onActivityState() | Activity state changes from the native side (activity-state; no-op where none). |
onActivityContent() | Activity content changes from the native side (activity-content, e.g. push updates). |
getWidgetPushToken() | WidgetKit push token (hex) for server-driven widget reloads; null where none exists. |
getActivityPushToStartToken() | iOS 17.2+ push-to-start token (hex): your server can start a Live Activity with it (APNs event: "start", attributes-type: "TauriActivityAttributes", attributes: {id, group}, content-state: {json: "<ActivityContent JSON>"}). null on other platforms or before iOS issued one; changes arrive via onActivityTokens. |
canPromoteActivities() | Android 16+: whether activity notifications may show as Live Updates (status-bar chip, lock screen). true where no such gate exists (iOS, desktop, older Android). |
openActivityPromotionSettings() | Android: open the system screen that allows Live Updates for this app; false if none. |
onActivityTokens() | Subscribe to Live Activity token changes (iOS only; a no-op elsewhere). |
listActivities() | Ids of running activities this app started. |
Functions
setItems()
async function setItems(key: string, value: string, group?: string): Promise<boolean>
Store a key-value pair in the widget data store.
The storage backend depends on the platform:
- Android —
SharedPreferencesscoped bygroup. - iOS / macOS — JSON file in App Group shared container.
- Desktop — JSON file in the app data directory (macOS: shared container when available).
key: Setting name (e.g."temperature").value: Setting value. UseJSON.stringify()for complex data.group: Widget group identifier: Android: SharedPreferences name; iOS/macOS: App Group ID (e.g."group.com.example.myapp"); Desktop: arbitrary string used as filename.
Source: guest-js/api/data.ts
getItems()
async function getItems(key: string, group?: string): Promise<string | null>
Read a previously stored value from the widget data store.
key: The key to look up.group: The widget group identifier.
Returns: The stored string value, or null if the key was never set.
Source: guest-js/api/data.ts
reloadAllTimelines()
async function reloadAllTimelines(): Promise<boolean>
Reload all registered widget timelines.
- Android — sends
ACTION_APPWIDGET_UPDATEbroadcast for every registered provider. - iOS / macOS — calls
WidgetCenter.shared.reloadAllTimelines(). - Desktop — emits a
"widget-reload"Tauri event that widget windows can listen for.
Source: guest-js/api/reload.ts
reloadTimelines()
async function reloadTimelines(ofKind: string): Promise<boolean>
Reload timelines for a specific widget kind or class.
ofKind: Widget class name (Android: fully-qualified class, e.g."com.example.app.MyWidget"; iOS: widget kind string from yourWidgetConfiguration).
Source: guest-js/api/reload.ts
setRegisterWidget()
async function setRegisterWidget(widgets: string[]): Promise<boolean>
Register widget provider class names or kind strings.
Must be called before reloadAllTimelines() or requestWidget().
widgets: Array of provider identifiers: - Android: fully-qualified class names (e.g."com.example.app.MyWidget") - iOS / macOS: widget kind strings from yourWidgetConfiguration
Source: guest-js/api/register.ts
requestWidget()
async function requestWidget(): Promise<boolean>
Request the OS to pin (add) a widget to the home screen.
- Android (API 26+) — calls
AppWidgetManager.requestPinAppWidget. - iOS / macOS / Desktop — no-op (users add widgets manually).
Source: guest-js/api/register.ts
createWidgetWindow()
async function createWidgetWindow(config: WidgetWindowConfig): Promise<boolean>
Create a frameless, transparent desktop widget window.
When no url is specified the plugin serves its built-in renderer
automatically — it reads the config via getWidgetConfig(group) and
renders it using vanilla HTML/CSS/SVG. The window has a drag handle
at the top and a close button that appears on hover.
You may provide your own url (e.g. "/my-widget.html") if you
need full control over the rendering.
Desktop only. Returns an error on mobile.
Example
// Built-in renderer (recommended):
await createWidgetWindow({
label: "weather",
width: 280,
height: 200,
group: "group.com.example.myapp",
widgetId: "weather",
size: "small",
});
// Custom renderer:
await createWidgetWindow({
label: "weather",
url: "/my-widget.html",
width: 280,
height: 200,
});
Source: guest-js/api/window.ts
closeWidgetWindow()
async function closeWidgetWindow(label: string): Promise<boolean>
Close a previously created desktop widget window.
label: The window label used when creating it. Desktop only.
Source: guest-js/api/window.ts
setWidgetConfig()
async function setWidgetConfig(config: WidgetConfig, /** Widget group. Omit to use `plugins.widgets.appGroup` (default `group.<identifier>`). */ group?: string, /** Widget id within the group; must match the native widget's `widgetId`. */ widgetId = "default", /** Skip native widget reload (WidgetKit / AppWidgetManager). * Desktop widget windows are always updated instantly via eval push. */ skipReload = false, /** One placement only (see `getActiveWidgets`): Android `appWidgetId`, the Apple Edit * Widget choice, a window `instance`. Other placements keep the widget-wide config. */ instance?: string): Promise<ApplyOutcome>
Send a declarative UI configuration to native widgets.
The JSON config is stored in the widget data store and read by the native widget extension, which renders it using platform-native components:
- iOS / macOS — SwiftUI views (
VStack,Text,Gauge, etc.) - Android —
RemoteViews(LinearLayout,TextView, etc.) - Desktop — HTML/CSS in the widget window
Returns ApplyOutcome: check written and reload.outcome — a
successful invoke no longer means a native reload was performed.
config: The widget UI configuration.group: Widget group identifier (same assetItemsgroup).
Example
const outcome = await setWidgetConfig({
small: {
type: "vstack",
padding: 12,
background: "#1a1a2e",
children: [
{ type: "text", content: "72°", fontSize: 36, fontWeight: "bold", color: "#fff" },
{ type: "progress", value: 0.7, tint: "#4CAF50" },
],
},
}, "group.com.example.myapp", "weather");
if (outcome.reload.outcome === "throttled") {
console.warn("reload delayed", outcome.reload.remainingSecs);
}
Source: guest-js/api/config.ts
setWidgetConfigFromPath()
async function setWidgetConfigFromPath(path: string, group?: string, widgetId = "default", skipReload = false, instance?: string): Promise<ApplyOutcome>
Read a WidgetConfig JSON file from disk and push it like setWidgetConfig.
Prefer this when the layout lives next to app resources instead of being built in code.
path must be readable by the app process (absolute path or a resolved
resourceDir() / app-data path). Network URLs are not supported.
Example
import { resourceDir, join } from "@tauri-apps/api/path";
import { setWidgetConfigFromPath } from "tauri-plugin-widgets-api";
const path = await join(await resourceDir(), "widgets", "weather.json");
await setWidgetConfigFromPath(path, "group.com.example.myapp", "weather");
Source: guest-js/api/config.ts
getWidgetConfig()
async function getWidgetConfig(group?: string, widgetId = "default", /** Read one placement's own config (no fallback to the widget-wide one). */ instance?: string): Promise<WidgetConfig | null>
Read the current widget UI configuration from the data store.
group: Widget group identifier. Omit to useplugins.widgets.appGroup(defaultgroup.<identifier>).widgetId: Widget identity within the group (default"default").
Returns: The current WidgetConfig, or null if none has been set.
Source: guest-js/api/config.ts
reportReceipt()
async function reportReceipt(receipt: WidgetRenderReceipt): Promise<boolean>
Report that a renderer painted a config (desktop widget.html / tests). Native widgets write receipts themselves; this is for the host webview path.
Source: guest-js/api/diagnostics.ts
getWidgetDiagnostics()
async function getWidgetDiagnostics(group?: string): Promise<WidgetRenderReceipt[]>
Live widget instances that recently rendered for group.
Source: guest-js/api/diagnostics.ts
getWidgetTrace()
async function getWidgetTrace(group?: string, opts?: { since?: number }): Promise<WidgetTrace>
Host delivery journal + receipt history for group.
Active in debug builds or when WIDGET_DEBUG=1.
Source: guest-js/api/diagnostics.ts
flushWidgetTrace()
async function flushWidgetTrace(): Promise<boolean>
Force-flush the in-memory journal to disk (desktop).
Source: guest-js/api/diagnostics.ts
widgetAction()
async function widgetAction(action: string, payload?: string, opts?: { widgetId?: string; group?: string }): Promise<boolean>
Trigger a widget-action event via the Rust plugin.
The Rust command broadcasts the event through app.emit(), so every
window that called onWidgetAction() (or listen("widget-action", …))
will receive it — including the main application window.
action: Action identifier (matchesButtonElement.action).payload: Optional extra string payload.
Source: guest-js/api/actions.ts
onWidgetAction()
async function onWidgetAction(callback: (data: WidgetActionPayload) => void): Promise<UnlistenFn>
Listen for widget-action events emitted by widget buttons.
callback: Called with the action payload each time a widget button with anactionfield is tapped.
Returns: A function to stop listening.
Example
import { onWidgetAction } from "tauri-plugin-widgets-api";
const unlisten = await onWidgetAction((data) => {
console.log("Widget action:", data.action, data.payload);
if (data.action === "refresh_data") {
fetchLatestData();
}
});
Source: guest-js/api/actions.ts
pollPendingWidgetActions()
async function pollPendingWidgetActions(group?: string): Promise<WidgetActionPayload[]>
Poll pending widget actions queued by native side (Android fallback path).
Source: guest-js/api/actions.ts
startWidgetUpdater()
async function startWidgetUpdater(builder: () => WidgetConfig | Promise<WidgetConfig>, /** Omit to use `plugins.widgets.appGroup`. */ group?: string, widgetId = "default", options?: { /** Tick interval in ms. Use `0` for a one-shot call (no periodic updates). Default: `5000`. */ intervalMs?: number; /** Call `builder` immediately before starting the interval. Default: `true`. */ immediate?: boolean; /** Also call `reloadAllTimelines()` after each tick. Default: `false`. * Backend throttles to once per 15 min on iOS/macOS. */ reload?: boolean; /** Subscribe to `widget-action` events for the lifetime of this updater. * The listener is automatically removed when the returned stop function is called. */ onAction?: (action: string, payload?: string) => void; }): Promise<() => void>
Start a periodic widget updater that calls builder on a fixed interval,
sends the returned config to the native widget, and optionally reloads
widget timelines.
This is a convenience wrapper around setInterval + setWidgetConfig +
reloadAllTimelines so you don't have to write boilerplate timers.
Important (iOS/macOS): Apple enforces a daily widget reload budget
(~40-70 reloads/day). The Rust backend throttles reloadAllTimelines()
to at most once every 15 minutes. For frequent data updates, set
reload: false (the default) — desktop webview widgets update instantly
via events without counting against the budget.
builder: Called everyintervalMs. Return the newWidgetConfig. May be async.group: App Group / SharedPreferences group identifier.options: Optional settings.options.intervalMs: Update interval in milliseconds (default5000). For native mobile widgets, consider using a longer interval (60000+) to avoid wasted work.options.immediate: Iftrue, run the builder immediately before the first interval tick (defaulttrue).options.reload: Iftrue, callreloadAllTimelines()after each update so native widgets refresh (defaultfalse). Set totruefor native widgets; leavefalsefor desktop-only. Note: backend throttles this to once per 15 min on iOS/macOS to respect the budget.
Returns: A stop() function. Call it to clear the timer.
Example
import { startWidgetUpdater } from "tauri-plugin-widgets-api";
const stop = await startWidgetUpdater(
() => ({
small: {
type: "vstack", padding: 12, background: "#1a1a2e",
children: [
{ type: "text", content: new Date().toLocaleTimeString(),
fontSize: 24, fontWeight: "bold", color: "#fff" },
],
},
}),
"group.com.example.myapp",
"clock",
{
intervalMs: 60_000,
reload: true,
onAction: (action, payload) => {
console.log("Widget action:", action, payload);
},
},
);
// later — stop updating and action listener:
stop();
Source: guest-js/api/updater.ts
getActiveWidgets()
async function getActiveWidgets(group?: string): Promise<ActiveWidget[]>
List placed widgets. Exact on Android (AppWidgetManager) and iOS (WidgetCenter);
on desktop: open widget windows plus placements that rendered in the last 48 h
(macOS WidgetKit, Windows Widgets Board).
Source: guest-js/api/instances.ts
setWidgetData()
async function setWidgetData(widgetId: string, data: unknown, options: { instance?: string; group?: string; skipReload?: boolean } = {}): Promise<boolean>
Values for {{data.path}} placeholders, without resending the layout. Pass null to
clear. Layers: config.data < this < the widget's own source fetch.
Example
await setWidgetConfig({ small: { type: "text", content: "{{data.temp}}°" } }, undefined, "weather");
await setWidgetData("weather", { temp: 21 });
Source: guest-js/api/instances.ts
setWidgetState()
async function setWidgetState(widgetId: string, patch: Record<string, unknown> | null, options: WidgetStateOptions = {}): Promise<Record<string, unknown>>
Change a widget's local state ({{state.…}}, config state defaults) the way a set
tap does: "$toggle", "$inc", "$dec", null removes a key (the default shows
again), other values are stored. patch: null clears the stored state. Resolves with
the merged state; the app also gets widget-state-changed {widgetId, instance, state}.
Example
await setWidgetState("tasks", { tab: "today", count: "$inc" });
Source: guest-js/api/instances.ts
getWidgetState()
async function getWidgetState(widgetId: string, options: Omit<WidgetStateOptions, "skipReload"> = {}): Promise<Record<string, unknown>>
A widget's merged local state: config state defaults, then what taps and
setWidgetState stored (the placement's own over the widget's).
Source: guest-js/api/instances.ts
setWidgetInstances()
async function setWidgetInstances(widgetId: string, instances: WidgetInstanceOption[], group?: string): Promise<boolean>
Choices the user picks from per placement. iOS / macOS: the Edit Widget sheet of a
widget built with TauriInstanceWidget / TauriInstanceProvider. The chosen id is the
placement's instance.
Source: guest-js/api/instances.ts
registerActivityTemplate()
async function registerActivityTemplate(name: string, layout: Partial<Omit<ActivityContent, "id" | "template" | "data">> | Record<string, unknown> | null, group?: string): Promise<boolean>
Store a live activity layout on the device (shared store / App Group) under name.
layout takes the ActivityContent layout fields (layout, lockScreen,
compactLeading, islandCenter, title, …) with {{data.…}}, when, repeat;
activities started with template: name then send only data. null removes it.
Example
await registerActivityTemplate("ride", {
title: "Driver {{data.driver}}",
lockScreen: { type: "text", content: "Arrives in {{data.eta}} min" },
});
await startActivity({ id: "ride-42", template: "ride", data: { driver: "Ann", eta: 4 } });
Source: guest-js/api/activity.ts
startActivity()
async function startActivity(content: ActivityContent, group?: string): Promise<ActivityHandle>
Start a live activity, or update it when id already runs.
Source: guest-js/api/activity.ts
updateActivity()
async function updateActivity(content: ActivityContent, group?: string): Promise<boolean>
Replace the content of a running activity.
Source: guest-js/api/activity.ts
endActivity()
async function endActivity(id: string, dismissAfterMs?: number, options: EndActivityOptions = {}): Promise<boolean>
End an activity; dismissAfterMs keeps the last state visible that long (iOS, desktop).
Source: guest-js/api/activity.ts
onActivityState()
async function onActivityState(cb: (e: ActivityStateEvent) => void): Promise<() => void>
Activity state changes from the native side (activity-state; no-op where none).
Source: guest-js/api/activity.ts
onActivityContent()
async function onActivityContent(cb: (e: ActivityContentEvent) => void): Promise<() => void>
Activity content changes from the native side (activity-content, e.g. push updates).
Source: guest-js/api/activity.ts
getWidgetPushToken()
async function getWidgetPushToken(group?: string): Promise<string | null>
WidgetKit push token (hex) for server-driven widget reloads; null where none exists.
Source: guest-js/api/activity.ts
getActivityPushToStartToken()
async function getActivityPushToStartToken(): Promise<string | null>
iOS 17.2+ push-to-start token (hex): your server can start a Live Activity with it
(APNs event: "start", attributes-type: "TauriActivityAttributes",
attributes: {id, group}, content-state: {json: "<ActivityContent JSON>"}).
null on other platforms or before iOS issued one; changes arrive via
onActivityTokens.
Source: guest-js/api/activity.ts
canPromoteActivities()
async function canPromoteActivities(): Promise<boolean>
Android 16+: whether activity notifications may show as Live Updates (status-bar chip,
lock screen). true where no such gate exists (iOS, desktop, older Android).
Source: guest-js/api/activity.ts
openActivityPromotionSettings()
async function openActivityPromotionSettings(): Promise<boolean>
Android: open the system screen that allows Live Updates for this app; false if none.
Source: guest-js/api/activity.ts
onActivityTokens()
async function onActivityTokens(cb: (e: ActivityTokenEvent) => void): Promise<() => void>
Subscribe to Live Activity token changes (iOS only; a no-op elsewhere).
Source: guest-js/api/activity.ts
listActivities()
async function listActivities(): Promise<string[]>
Ids of running activities this app started.
Source: guest-js/api/activity.ts
Types
PLUGIN_ID
Shared plugin invoke prefix.
const PLUGIN_ID = "plugin:widgets";
WidgetWindowConfig
Configuration for creating a desktop widget window.
| Property | Type | Description |
|---|---|---|
label | string | Unique window label (e.g. "cpu-widget"). Used to reference the window later. |
url? | string | Frontend route to load. When omitted (or empty) the plugin serves its built-in widget renderer automatically — no extra files needed. Set to a path (e.g. "/widget.html") only if you want to use your own custom renderer. |
width | number | Window width in logical pixels. |
height | number | Window height in logical pixels. |
x? | number | X position on screen (pixels from left). |
y? | number | Y position on screen (pixels from top). |
alwaysOnTop? | boolean | Keep above all other windows. Default: false. |
skipTaskbar? | boolean | Hide from taskbar / dock. Default: true. |
group? | string | Widget group identifier. Required when using the built-in renderer — tells it which config to load via getWidgetConfig. Ignored when a custom url is provided. |
widgetId? | string | Widget identity within the group. Required for the built-in renderer. |
instance? | string | Placement id: the built-in renderer reads config:{widgetId}#{instance} first, fills {{instance}}, and getActiveWidgets reports it. Default: the window label. |
size? | "small" | "medium" | "large" | Size family the built-in renderer should display: "small", "medium", or "large". Defaults to "small". |
layer? | WidgetWindowLayer | Stacking layer. Default: "top" when alwaysOnTop, else "desktop" on Linux and "normal" on macOS / Windows. "desktop": X11 DESKTOP type, Wayland layer-shell Background, macOS desktop level, Windows behind the desktop icons. |
clickThrough? | boolean | Let clicks pass through to whatever is below. Default false. |
focusable? | boolean | Whether the window can take keyboard focus. Default true. |
allWorkspaces? | boolean | Show on every virtual desktop / Space. Default: true for the desktop layer. |
shadow? | boolean | System window shadow (macOS / Windows). |
effects? | "vibrancy" | "mica" | "acrylic" | "blur" | Backdrop material hint (needs a transparent window; ignored on Linux). |
placement? | WidgetPlacement | Place relative to a monitor's work area instead of x / y. |
rememberPosition? | boolean | Remember where the user moved the window (per label) and reopen it there while that spot is on a connected monitor. Default true. |
WidgetWindowLayer
Stacking layer of a desktop widget window.
type WidgetWindowLayer = "normal" | "top" | "bottom" | "desktop";
WidgetPlacement
Monitor-relative placement for WidgetWindowConfig.placement.
| Property | Type | Description |
|---|---|---|
monitor? | number | string | Monitor index (availableMonitors order) or name. Default: primary. |
anchor? | | "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right" | Work-area corner / edge. Default "top-left". |
offsetX? | number | Logical px inward from the anchored vertical edge. Default 0. |
offsetY? | number | Logical px inward from the anchored horizontal edge. Default 0. |
SkipReason
Why a config write was skipped.
type SkipReason =
| { reason: "unchanged"; hash: number }
| { reason: "noInstances" }
| { reason: "transportUnavailable"; name: string };
ReloadOutcome
Result of a native WidgetKit / AppWidget reload attempt.
type ReloadOutcome =
| { outcome: "ok" }
| { outcome: "throttled"; remainingSecs: number }
| { outcome: "skipped"; why: string }
| { outcome: "failed"; error: string };
ApplyOutcome
Full result of setWidgetConfig. Never treat success as “reload happened”.
| Property | Type | Description |
|---|---|---|
written | boolean | true when store bytes changed and were written. |
reload | ReloadOutcome | |
transports | string[] | Transport names that received the map (empty when not written). |
skip? | SkipReason |
SkippedElement
Element the renderer skipped, with reason.
| Property | Type | Description |
|---|---|---|
type | string | |
reason | string |
WidgetRenderReceipt
Cross-platform render receipt (diagnostics — not a critical path).
| Property | Type | Description |
|---|---|---|
widgetId | string | |
group | string | |
instance | string | |
nonce | number | |
size? | string | |
theme? | string | |
schema? | number | |
source | string | prefs | state | appgroup | defaults | container | push | pull |
trigger? | string | reload | timeline | action | added | resize | snapshot |
rendered? | string[] | |
skipped? | SkippedElement[] | |
ts | number |
TraceEvent
One host journal entry (WIDGET_DEBUG / debug builds).
type TraceEvent =
| {
kind: "configSet";
widgetId: string;
nonce: number;
bytes: number;
changed: boolean;
skip?: { reason: "unchanged"; hash: number } | { reason: "noInstances" } | {
reason: "transportUnavailable";
name: string;
};
}
| {
kind: "write";
transport: string;
ok: boolean;
durationMs: number;
error?: string;
}
| { kind: "reload"; performed: boolean; reason: ReloadOutcome }
| { kind: "poll"; count: number }
| {
kind: "render";
instance: string;
nonce: number;
source: string;
trigger: string;
lagMs: number;
skipped: SkippedElement[];
};
TraceEntry
| Property | Type | Description |
|---|---|---|
ts | number | |
kind | TraceEvent["kind"] | |
[key: string]: unknown |
WidgetTrace
| Property | Type | Description |
|---|---|---|
enabled | boolean | |
events | TraceEntry[] | |
receipts | WidgetRenderReceipt[] |
WidgetActionPayload
Payload delivered by the widget-action event.
| Property | Type | Description |
|---|---|---|
action | string | The action identifier from the button config. |
payload? | string | null | Optional extra data (e.g. serialized JSON from the widget). null when the widget sent none. |
ts? | number | Unix epoch ms when the action was enqueued / emitted. |
widgetId? | string | Widget identity that emitted the action. |
group? | string | App group / prefs namespace. |
ActiveWidget
A widget placed right now on a home screen, desktop or Widgets Board.
| Property | Type | Description |
|---|---|---|
widgetId | string | Logical widget id (WidgetKit kind / config widgetId). |
instance? | string | Placement id: Android appWidgetId, the Apple Edit Widget choice, a window instance / label, a Widgets Board id. Absent when the placement has none. |
size? | string | small | medium | large when known. |
lastSeenMs? | number | Unix ms of the last render, when known from render receipts. |
WidgetInstanceOption
A choice in the Apple Edit Widget sheet (e.g. a city).
| Property | Type | Description |
|---|---|---|
id | string | Placement id: {{instance}}, setWidgetConfig(…, instance). |
title | string | Text shown to the user. |
WidgetStateOptions
Where a widget's local state lives: one placement (instance) or all of them.
type WidgetStateOptions = { instance?: string; group?: string; skipReload?: boolean };
ActivityAction
A button of a live activity; taps arrive as widget-action with widgetId: "activity.<id>".
| Property | Type | Description |
|---|---|---|
id | string | |
label | string | |
systemName? | string | Icon (SF Symbol; Material / emoji elsewhere). |
url? | string | Open this deep link instead of queuing a widget-action. |
ActivityContent
A live activity: iOS Live Activity (Lock Screen, Dynamic Island), Android ongoing
notification (Live Update chip on Android 16), desktop always-on-top widget window.
Without layout one is built from title / body / progress / endsAt / actions.
The serialized request — including the layout the plugin builds when layout is
omitted — must stay under 4 KiB (the ActivityKit limit, checked on every OS).
| Property | Type | Description |
|---|---|---|
id | string | Your key (letters, digits, ., _, -). |
title? | string | Required unless the template gives it. |
body? | string | |
progress? | number | 0..1. |
endsAt? | number | Unix ms the countdown runs to. |
icon? | string | SF Symbol name (Material / emoji off Apple). |
tint? | string | Hex accent color. |
layout? | WidgetElement | |
compactLeading? | WidgetElement | |
compactTrailing? | WidgetElement | |
minimal? | WidgetElement | |
lockScreen? | WidgetElement | iOS Lock Screen layout when it should differ from layout. |
islandLeading? | WidgetElement | Expanded Dynamic Island regions. |
islandTrailing? | WidgetElement | |
islandCenter? | WidgetElement | |
keylineTint? | string | Hex colors: Dynamic Island keyline, Lock Screen background. |
backgroundTint? | string | |
subText? | string | Android: notification sub text; Android 16 status-bar chip text (≤ 7 chars). |
shortCriticalText? | string | |
startedAt? | number | Unix ms: a stopwatch counts up from here when endsAt is unset. |
visibility? | "public" | "private" | "secret" | Android lock-screen visibility. |
category? | "progress" | "navigation" | "transport" | "workout" | "status" | "alarm" | "service" | "event" | Android notification category. |
actions? | ActivityAction[] | At most 3. |
url? | string | Deep link opened on tap. |
staleAt? | number | Unix ms after which the content counts as outdated (iOS). |
push? | boolean | iOS: ask for an APNs token (pushToken, activity-token event). |
template? | string | Name given to registerActivityTemplate: the device fills that layout with data, so the request (and its 4 KiB budget) carries only data. Fields set here override the template's. |
data? | Record<string, unknown> | Values for {{data.…}} in the template. |
relevanceScore? | number | iOS: which of several activities the system shows first (higher wins). |
alert? | { title: string; body: string; sound?: string } | iOS: alert shown with this update (sound: a file in the app bundle). |
timestamp? | number | Unix ms the content was produced; an update older than the shown one is dropped. |
ActivityHandle
| Property | Type | Description |
|---|---|---|
id | string | |
surface | string | ios | android | desktop. |
pushToken? | string |
EndActivityOptions
Options of endActivity.
| Property | Type | Description |
|---|---|---|
content? | ActivityContent | Final content shown while the activity is dismissed. |
dismissalPolicy? | "default" | "immediate" | immediate removes it at once; default lets the OS keep it a while (iOS: up to 4 h). dismissAfterMs wins over it. |
group? | string |
ActivityStateEvent
Lifecycle of an activity reported by the OS.
type ActivityStateEvent = { id: string; state: "active" | "ended" | "dismissed" | "stale" | string };
ActivityContentEvent
Content the OS shows now (for example after a push update).
type ActivityContentEvent = { id: string; content: ActivityContent };
ActivityTokenEvent
Token events from iOS: per-activity APNs tokens and push-to-start token updates.
type ActivityTokenEvent =
| { kind: "activity"; id: string; pushToken: string }
| { kind: "pushToStart"; pushToStartToken: string };