React Native Step Counter Widget for iOS: How It Works
The app is React Native; the widget is a native SwiftUI extension. They share data through an App Group, not props.
TL;DR
An iOS home-screen step counter widget cannot be written in React Native, because home-screen widgets are native WidgetKit extensions in SwiftUI. The working pattern is: your React Native app reads steps (from CMPedometer for live counts or HealthKit for history), writes the latest number to a shared App Group, and a small SwiftUI WidgetKit extension reads that shared value and renders the widget. Widget refreshes are budgeted by iOS, so you update on a timeline, not in real time. Tools like Expo's native targets make wiring the extension manageable. Start the in-app screens from a free VP0 design at $0.
A home-screen step counter widget sounds like a React Native feature and is not, quite. Home-screen widgets on iOS are WidgetKit extensions written in SwiftUI, and React Native cannot render into them. So the question “how do I build a React Native step widget” has a precise answer: you keep the app in React Native and add a small native widget that shares data with it. Here is how the pieces fit, where the step data comes from, and the refresh limit that surprises people. Start the in-app screens from a free VP0 design (the free iOS and React Native design library AI builders read from) at $0.
The architecture: two targets, one shared box
The widget and the app are separate targets that never talk directly. They share a container:
| Piece | Tech | Role |
|---|---|---|
| Main app | React Native | Reads steps, writes the latest value |
| Shared store | App Group | The box both targets can read and write |
| Widget | SwiftUI WidgetKit extension | Reads the shared value, renders the widget |
The React Native app reads the step count and writes it into a shared App Group (its shared UserDefaults or a shared file). The WidgetKit extension reads that value when it builds its timeline. Get the App Group wrong and the widget shows nothing, which is the most common “my widget is blank” cause.
Where the steps come from
Two iOS sources, used together. Core Motion’s CMPedometer gives live and recent step counts straight from the motion coprocessor, which is what you want for today’s number. HealthKit gives historical samples across days and devices, which is what you want for trends and the in-app chart. A typical app uses CMPedometer for the current count, writes it to the App Group for the widget, and uses HealthKit for the history screen. The in-app step UI mirrors the Apple HealthKit step counter SwiftUI template and the HealthKit step counter clone UI.
The refresh limit nobody expects
Widgets do not update in real time. iOS budgets widget refreshes and runs them on a timeline you provide, deciding itself when to execute reloads to save power. So a step widget updates periodically, not the instant you take a step. Build for that honestly: show the last known value with its timestamp, and request reloads at sensible points rather than expecting live counts. The widget’s visual layout follows the same constraints as the iOS lock screen widget UI template. Wiring a native extension into a React Native project is the fiddly part; Expo’s native targets approach makes adding the WidgetKit extension manageable without ejecting your whole workflow. While you are in the layout, keep it localization-ready per the React Native RTL flexbox fix, and if the app also takes payments, the iDEAL payment bank selector UI keeps that data in its own store.
Key takeaways
- iOS home-screen widgets are native SwiftUI WidgetKit extensions; React Native cannot render them.
- The pattern is a React Native app plus a native widget that share an App Group.
- Step data comes from CMPedometer (live) and HealthKit (history); write the live count to the App Group.
- Widgets refresh on a system-budgeted timeline, not in real time; show the last value and its time.
- Use Expo’s native targets to add the extension, and start the in-app screens from a free VP0 design at $0.
Frequently asked questions
Can you build an iOS home-screen widget in React Native?
Not the widget itself. Home-screen widgets on iOS are WidgetKit extensions written in SwiftUI, and React Native cannot render into them. What React Native can do is own the main app and share data with a small native widget extension through an App Group. So the pattern is a React Native app plus a native SwiftUI widget that reads shared data, not a widget written in JavaScript.
How does a React Native app send step data to an iOS widget?
Through a shared App Group. The app reads the step count (from CMPedometer for live data or HealthKit for history), writes the latest value into the App Group’s shared UserDefaults or a shared file, and the WidgetKit extension reads that value when it builds its timeline. The two targets do not talk directly; the App Group is the shared container between them.
Where do step counts come from on iOS?
Two sources. Core Motion’s CMPedometer gives live and recent step counts straight from the motion coprocessor, good for today’s steps. HealthKit gives historical step samples across days and devices, good for trends. Many apps use CMPedometer for the current number and HealthKit for history, and write the current number into the App Group for the widget.
Why does my iOS widget not update instantly?
By design. iOS budgets widget refreshes and runs them on a timeline rather than in real time, so a widget updates periodically, not the instant steps change. You provide a timeline of entries and request reloads, and the system decides when to run them. Build for that: show the last known value with its timestamp rather than expecting live updates.
What is the best way to build a step widget for a React Native app?
Keep the app in React Native, add a native SwiftUI WidgetKit extension, and share data through an App Group, using a tool like Expo’s native targets to manage the extension. Start the in-app screens from a clean layout: a free VP0 design, the free iOS and React Native design library for AI builders, gives you the step UI to generate in Cursor or Claude Code at $0.
Questions VP0 users ask
Can you build an iOS home-screen widget in React Native?
Not the widget itself. Home-screen widgets on iOS are WidgetKit extensions written in SwiftUI, and React Native cannot render into them. What React Native can do is own the main app and share data with a small native widget extension through an App Group. So the pattern is a React Native app plus a native SwiftUI widget that reads shared data, not a widget written in JavaScript.
How does a React Native app send step data to an iOS widget?
Through a shared App Group. The app reads the step count (from CMPedometer for live data or HealthKit for history), writes the latest value into the App Group's shared UserDefaults or a shared file, and the WidgetKit extension reads that value when it builds its timeline. The two targets do not talk directly; the App Group is the shared container between them.
Where do step counts come from on iOS?
Two sources. Core Motion's CMPedometer gives live and recent step counts straight from the motion coprocessor, good for today's steps. HealthKit gives historical step samples across days and devices, good for trends. Many apps use CMPedometer for the current number and HealthKit for history, and write the current number into the App Group for the widget.
Why does my iOS widget not update instantly?
By design. iOS budgets widget refreshes and runs them on a timeline rather than in real time, so a widget updates periodically, not the instant steps change. You provide a timeline of entries and request reloads, and the system decides when to run them. Build for that: show the last known value with its timestamp rather than expecting live updates.
What is the best way to build a step widget for a React Native app?
Keep the app in React Native, add a native SwiftUI WidgetKit extension, and share data through an App Group, using a tool like Expo's native targets to manage the extension. Start the in-app screens from a clean layout: a free VP0 design, the free iOS and React Native design library for AI builders, gives you the step UI to generate in Cursor or Claude Code at $0.
Part of the React Native & Expo: Mobile Frontend Architecture hub. Browse all VP0 topics →
Keep reading
Expo Background Tasks UI: Processing Without Promises
Build Expo background tasks and the UI around them: the opportunistic-scheduling truth, what fits the budget, honest toggle copy, and last-synced timestamps.
React Native Deep Linking and the Unhandled URL UI
How to handle deep linking in React Native and Expo, with a graceful unhandled-URL fallback instead of a blank app when a link matches no route.
Full-Stack React Native Expo + Supabase Template, Free
Want a full-stack React Native Expo + Supabase starter? Generate your own from a free design plus Supabase auth, database, and storage, with Claude Code or Cursor.
Port Vercel v0 Components to React Native and Expo
v0 outputs React web with Tailwind. Here is how to map its components into a React Native and Expo iOS app with NativeWind, plus the pitfalls to avoid.
Bolt.new React Router Errors in Expo? Swap the Router
Bolt.new app throwing React Router DOM errors when you move to Expo mobile? React Router is for the web. Replace it with Expo Router or React Navigation.
Build a Responsive iPhone-to-iPad Layout in React Native
A responsive tablet layout changes shape, it does not just scale up. Here is how to build an adaptive iPhone-to-iPad layout in React Native with breakpoints.