expo-widgets Home Screen Widgets
iOS WidgetKit, Live Activities, and data synchronization patterns with Expo.
Key takeaways
expo-widgetsdefines iOS home screen widgets and Live Activities in React, and its Config Plugin generates the Widget Extension, App Group, and entitlements during prebuild.- The SDK 56 changelog marks widgets and Live Activities stable while the API reference still shows an alpha banner, so pin versions and keep device regression tests.
- Define widgets with
createWidget, matching the component name towidgets[].name, and write the'widget'directive inside the render function. - Live Activities use
createLiveActivitywithstart/update/end, plusgetPushTokenand push-to-start listeners for server-triggered flows. - Keep App Group data small and serializable, never store images, tokens, or PII, and discard stale
contentDatepush updates.
Overview
expo-widgets lets teams define iOS home screen widgets and Live Activities with React components
and Expo UI. The Config Plugin creates the Widget Extension, App Group, and entitlements during
prebuild.
How to read the SDK 56 status
The SDK 56 changelog says iOS Widgets and Live Activities have moved to stable. The current API reference still shows an alpha banner. For production-critical features, pin package versions and keep real device regression tests.
Architecture
Config Plugin Baseline
{
"expo": {
"plugins": [
[
"expo-widgets",
{
"groupIdentifier": "group.com.company.app",
"enablePushNotifications": true,
"widgets": [
{
"name": "StatusWidget",
"displayName": "Status",
"description": "Shows the most important status at a glance.",
"supportedFamilies": ["systemSmall", "systemMedium"]
}
]
}
]
]
}
}Keep this plugin configuration as the source of truth. Manual native target edits can be lost when Continuous Native Generation regenerates projects.
Widget Component Pattern
import { Text, VStack } from '@expo/ui/swift-ui';
import { font } from '@expo/ui/swift-ui/modifiers';
import { createWidget, type WidgetEnvironment } from 'expo-widgets';
type StatusWidgetProps = {
label: string;
count: number;
};
function StatusWidget(props: StatusWidgetProps, env: WidgetEnvironment) {
'widget';
return (
<VStack>
<Text modifiers={[font({ weight: 'bold', size: 16 })]}>{props.label}</Text>
<Text>{props.count}</Text>
<Text>{env.widgetFamily}</Text>
</VStack>
);
}
export default createWidget('StatusWidget', StatusWidget);The widget name must match widgets[].name in app config.
Live Activities
Live Activities show real-time information on the lock screen and Dynamic Island.
| Pattern | Purpose |
|---|---|
createLiveActivity | create the Live Activity factory |
start(props, deepLink) | start an activity from the app |
update(props) | update the activity state |
end(policy, props, contentDate) | end and prevent stale payloads |
getPushToken() | get the activity-specific APNs token |
addPushToStartTokenListener | support server-triggered push-to-start flows |
Production Data Rules
- Keep widget props small and serializable.
- Do not store large images, tokens, or PII in the App Group.
- Use one route contract for widgets, Live Activities, and push notifications.
- Include server timestamps in Live Activity push payloads and discard stale
contentDateupdates. - Capture
systemSmall,systemMedium, and lock screen accessory families separately.
Caveats
- The changelog and API reference status labels are not perfectly aligned. Check both every release.
- iOS is the current target. Android Glance support needs separate native work or future Expo support.
- Test in development builds, not only Expo Go.
- Widget networking and interactive elements are constrained.
- App Group storage is for lightweight state only.