Skip to main content

Button

A tappable component that runs a piece of JS when tapped.

Can't open URLs

Button can only run JS — it can't open URLs directly (it's built on AppIntent, a system limitation). For "tap to navigate", use a stack container as a button — vstack / hstack / zstack / tstack / grid all have an "Open URL" property, and the entire container becomes tappable.

Platform limits

Buttons on home screen widgets require iOS 17+. Lock screen widgets and Dynamic Island widgets currently can't host tappable buttons (system limitation).

Usage

Tap the ➕ in the top-right of the editor and add button. You can place any children inside it (text / icon / image) to define its appearance. Then in the button's properties, write the JS to run.

Common properties

PropertyDescription
ChildrenThe button's visuals (text, icon, etc.)
Content (JS code)The JS snippet run on tap
Styleplain (no border) / noflash (no flash) / normal (system button)
Background / Corner radiusVisual tweaks

Button code vs. main JS

Main JSButton code
When it runsWhen the widget refreshesWhen the user taps the button
Used forFetching data / API calls, assigning to this.xxxChanging local state / triggering actions
LengthUsually longerUsually a few lines

After the button code runs, Omni automatically refreshes the widget so the new state takes effect.

Example: toggle a "liked" state

Main JS:

this.state = getValue("liked") || "heart"

Button code:

const cur = getValue("liked") || "heart"
setValue("liked", cur === "heart" ? "heart.fill" : "heart")

Inside the button, place an icon with content {state} — each tap toggles between "♡" and "❤".

Want "tap to open a URL"?

Don't use a button — use a stack container instead:

zstack (Open URL: https://2f0.cn)
└─ text ("Open site")

Or open a third-party app:

hstack (Open URL: weixin://)
├─ icon (message.fill)
└─ text ("Open WeChat")

More in stack.md → Open URL.