RORK LABJP
RORK MAX — Rork Max can now build native Swift apps for iPhone, iPad, Apple Watch, Apple TV, and Vision ProPUBLISH — Rork Max offers two-click App Store publishing with no Xcode required, cutting the friction of getting an app shippedEXPO — The standard Rork is built on React Native (Expo), generating native iOS and Android apps from plain-English descriptionsPRICING — Rork is free to start, with paid plans beginning at $25/month, an accessible tier for solo developersFUNDING — Rork raised $2.8M from a16z (Andreessen Horowitz) as investment keeps flowing into AI app buildersREVIEW — In real use the keys are generated-code readability and maintainability, Expo-related constraints, and how easily billing, push, and ad SDKs slot inRORK MAX — Rork Max can now build native Swift apps for iPhone, iPad, Apple Watch, Apple TV, and Vision ProPUBLISH — Rork Max offers two-click App Store publishing with no Xcode required, cutting the friction of getting an app shippedEXPO — The standard Rork is built on React Native (Expo), generating native iOS and Android apps from plain-English descriptionsPRICING — Rork is free to start, with paid plans beginning at $25/month, an accessible tier for solo developersFUNDING — Rork raised $2.8M from a16z (Andreessen Horowitz) as investment keeps flowing into AI app buildersREVIEW — In real use the keys are generated-code readability and maintainability, Expo-related constraints, and how easily billing, push, and ad SDKs slot in
Articles/Dev Tools
Dev Tools/2026-06-15Advanced

Putting a Working Button in a Rork App's Widget — Implementing App Intents So a Tap Acts Without Opening the App

How to put a button in a Rork-generated Expo app's widget that changes state without launching the app. We wire App Intents and WidgetKit together through an App Group, all the way to reloadTimelines — including the two places I lost real time.

Rork403App Intents5WidgetKit6Expo74iOS79

Premium Article

One morning I was looking at my habit-tracker widget and my hand just stopped. To tick a single item, I open the app, wait for a load screen, scroll to the row, then tap. Almost five seconds for one piece of one-tap data. If I could check it directly on the widget, those five seconds would become zero.

Since iOS 17, a button inside a widget can run work without launching the app. I knew the mechanism. But making it happen inside a Rork-generated, Expo-based app turned out to be far more involved than I expected — because the React Native world and the widget world run in different processes, in different languages.

This walks through getting the full round-trip working in a Rork app: tap a button in the widget, update state without opening the app, and have the widget redraw immediately. I'll leave the two places I got stuck explicit.

Why "add a button" doesn't work

Start with the structure. A home screen widget runs as an App Extension — a separate target from your app. There is no JavaScript engine inside that extension process. Not a single line of React Native runs there. That's exactly why asking Rork's chat to "add a button to my widget" gives you a button on an in-app screen, never one that completes its work on the home screen.

For a button inside a widget to act without launching the app, you have to provide and mesh together three parts yourself.

First, an App Intent — a unit of work you can hand to Button(intent:) that the system performs behind the scenes. Second, an App Group — the shared store that lets the app and the extension read and write the same data. Third, a timeline reload — the trigger that makes the widget redraw after the intent changes state.

Standard Rork (Expo-based) provides none of these automatically. Rork Max (which generates native Swift) can scaffold the whole WidgetKit extension for you, but if you're bolting this onto an app already shipped on Expo, the config-plugin route described here is the realistic one.

Get the App Group working first

Order matters. Before the button, make the app and the extension able to see the same data. If this isn't solid, you later get the worst kind of bug: the button fires, but nothing on screen changes, and the cause is hard to isolate.

Pick an App Group identifier (e.g. group.net.rorklab.habit). In your Apple Developer target settings, enable the same App Group on both the app and the extension. In Expo, add it to the entitlements in app.json.

{
  "expo": {
    "ios": {
      "entitlements": {
        "com.apple.security.application-groups": [
          "group.net.rorklab.habit"
        ]
      }
    }
  }
}

To write into this shared store from React Native, you need a native module that talks to UserDefaults(suiteName:) — a library like expo-shared-defaults, or a thin bridge added through a config plugin. The minimal Swift bridge looks like this.

import Foundation
 
@objc(SharedDefaults)
class SharedDefaults: NSObject {
  static let suite = UserDefaults(suiteName: "group.net.rorklab.habit")
 
  @objc func setBool(_ value: Bool, key: String) {
    SharedDefaults.suite?.set(value, forKey: key)
  }
 
  @objc func getBool(_ key: String, resolver resolve: RCTPromiseResolveBlock,
                     rejecter reject: RCTPromiseRejectBlock) {
    resolve(SharedDefaults.suite?.bool(forKey: key) ?? false)
  }
}

Now you have a two-way foundation: the widget extension can read a value the JS side wrote, and the JS side can read a value the extension wrote. Verify this round-trip with a single boolean first — it makes everything downstream far easier.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Understand, with working Swift, how a button inside a widget runs without launching the app — the AppIntent / perform round-trip
A concrete path to connect a Rork Expo app and its extension target through an App Group: write-back, reloadTimelines, redraw
Take home the two traps that cost me two hours — taps doing nothing, and state rolling back — plus how to avoid each
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-06-12
Adding a Home Screen Widget to a Rork App — Making WidgetKit Work Within Expo's Constraints
Rork generates Expo apps, and home screen widgets can't be written in React Native. Here's how to wire up WidgetKit with a config plugin and App Groups — including the parts that tripped me up.
Dev Tools2026-04-23
Integrating Siri and Shortcuts into Rork Apps: App Intents with Expo, Without the Pitfalls
A production-grade guide to adding Siri Shortcuts and App Intents to a React Native app generated by Rork. Bridge native Swift via an Expo Config Plugin, model AppShortcut / AppEntity / EntityQuery correctly, and ship Donations and Control Center widgets without breaking Apple's conventions.
Dev Tools2026-06-14
Actually Delivering 'It Updates Without Opening' in Expo — A Realistic Background Task Design
Building 'content refreshes every morning' into a Rork-generated Expo app runs into iOS background execution being far less dutiful than you expect. Here is a minimal expo-background-task setup plus a design that doesn't break when the task never runs.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →