This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
Expo 小组件
一个使用 Expo UI 组件构建 iOS 主屏幕小组件和实时活动的库。
重要 此库在 Expo Go 应用中不可用——请使用开发构建来试用它。
expo-widgets enables the creation of iOS home screen widgets and Live Activities using Expo UI components, without writing native code. It provides a simple API for creating and updating a widget's timeline, as well as starting and managing Live Activities. You can build the layout using expo/ui components and modifiers.
Known limitations
- Frequent Live Activity updates. To raise the budget for frequent push updates, set
NSSupportsLiveActivitiesFrequentUpdatestotruein your Info.plist. The system may still throttle updates, and the user can disable frequent updates in Settings. - Widget runtime. Code inside a
'widget'-marked component runs in an isolated runtime and can only use@expo/ui/swift-uicomponents, with no React hooks, app state, or asynchronous work. See The'widget'directive.
安装
- npx expo install expo-widgetsIf you are installing this in an existing React Native app, make sure to install expo in your project.
在 app 配置中进行配置
如果你的项目中使用了配置插件(Continuous Native Generation (CNG)),你可以使用 expo-widgets 内置的配置插件进行配置。该插件允许你配置一些无法在运行时设置、且需要重新构建新的应用二进制文件才能生效的属性。
Example app.json with config plugin
{ "expo": { "plugins": [ [ "expo-widgets", { "widgets": [ { "name": "MyWidget", "displayName": "My Widget", "description": "A sample home screen widget", "ios": { "supportedFamilies": ["systemSmall", "systemMedium", "systemLarge"] } } ] } ] ] } }
Configurable properties
| Name | Default | Description |
|---|---|---|
bundleIdentifier | "<app bundle identifier>.ExpoWidgetsTarget" | 小组件扩展目标的 bundle 标识符。如果未指定,默认值为 |
groupIdentifier | "group.<app bundle identifier>" | The app group identifier used for communication and data sharing between the main app and widgets, which widgets require to work. If not specified, it defaults to |
enablePushNotifications | false | 是否为 Live Activities 启用推送通知。启用后,将添加 |
widgets | - | 小组件配置数组。数组中的每个小组件都会在你的小组件扩展中生成为一个独立的小组件类型。 |
widgets[].name | - | The internal name (identifier) of the widget. This is used as the Swift struct name and should be a valid Swift identifier (no spaces or special characters). It must match the |
widgets[].displayName | - | 小组件在用户将其添加到主屏幕时,会出现在小组件库中的面向用户名称。 |
widgets[].description | - | 对小组件功能的简要描述。它会显示在小组件库中,帮助用户理解该小组件的用途。 |
widgets[].ios.supportedFamilies | - | 此小组件支持的小组件尺寸数组。可用选项:
|
widgets[].ios.contentMarginsDisabled | false | When you disable content margins for a widget, the system doesn't automatically add margins around the widget's content, and you are responsible for specifying margins and padding around your widget content for each context. |
widgets[].ios.configuration | - | Makes the widget configurable. The values the user picks are passed to your widget at runtime through
|
The top-levelsupportedFamiliesandcontentMarginsDisabledoptions are deprecated aliases forios.supportedFamiliesandios.contentMarginsDisabled. Prefer the nestediosform shown above.
Full example with all options
{ "expo": { "plugins": [ [ "expo-widgets", { "bundleIdentifier": "com.example.myapp.widgets", "groupIdentifier": "group.com.example.myapp", "enablePushNotifications": true, "widgets": [ { "name": "StatusWidget", "displayName": "Status", "description": "Shows your current status at a glance", "ios": { "contentMarginsDisabled": true, "supportedFamilies": ["systemSmall", "systemMedium"] } }, { "name": "WeatherWidget", "displayName": "Weather", "description": "Shows the weather for a city you choose", "ios": { "supportedFamilies": ["systemSmall", "systemMedium"], "configuration": { "title": "Choose a city", "description": "Pick which city to show the weather for", "parameters": { "city": { "title": "City", "type": "enum", "default": "sf", "values": [ { "name": "San Francisco", "value": "sf" }, { "name": "New York", "value": "nyc" } ] } } } } }, { "name": "LockScreenWidget", "displayName": "Quick View", "description": "View info on your Lock Screen", "ios": { "supportedFamilies": [ "accessoryCircular", "accessoryRectangular", "accessoryInline" ] } } ] } ] ] } }
使用
The 'widget' directive
Components passed to createWidget and createLiveActivity must start with the 'widget' directive. The directive tells the bundler to compile that component into a separate JavaScript bundle that runs in an isolated runtime inside the widget extension, not in your app's React Native runtime.
Because of this isolation, code inside a 'widget'-marked component is limited:
- It can only render
@expo/ui/swift-uicomponents and modifiers. Standard React Native components (such asViewandTextfromreact-native) are not available. - It cannot use React hooks (
useState,useEffect, and others), component state, or context. The function must be pure and return its layout synchronously. - It cannot perform asynchronous work, import other modules, or access your app's runtime or in-memory state.
- It cannot reference anything declared outside the component function, including plain top-level
consts in the same file. The bundler serializes only the function body, so module-scope values are not present at runtime. Declare every constant and helper inside the widget function, or pass it in through props.
All data a widget needs must come in through its props (set with updateSnapshot, updateTimeline, or a Live Activity's start and update) and the environment argument. To use images, write them to widgetsDirectory from your app and reference them by path.
import { Text } from '@expo/ui/swift-ui'; import { createWidget, type WidgetEnvironment } from 'expo-widgets'; // Declared at module scope — not included in the widget bundle. const CITY_NAMES: Record<string, string> = { sf: 'San Francisco' }; const CityWidget = (props: object, environment: WidgetEnvironment<{ city: string }>) => { 'widget'; // Throws at runtime: Can't find variable: CITY_NAMES return <Text>{CITY_NAMES[environment.configuration.city]}</Text>; }; export default createWidget('CityWidget', CityWidget);
Move CITY_NAMES inside CityWidget (or pass the resolved value through props) to fix it.
Widgets
Prerequisite: Creating a widget
Start by creating a widget using the createWidget function and pass the widget component marked with the 'widget' directive. The component receives your widget props as the first argument and a WidgetEnvironment object as the second.
import { Text, VStack } from '@expo/ui/swift-ui'; import { font, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; import { createWidget, type WidgetEnvironment } from 'expo-widgets'; type MyWidgetProps = { count: number; }; const MyWidget = (props: MyWidgetProps, environment: WidgetEnvironment) => { 'widget'; return ( <VStack> <Text modifiers={[font({ weight: 'bold', size: 16 }), foregroundStyle('#000000')]}> 计数: {props.count} </Text> <Text>类型: {environment.widgetFamily}</Text> </VStack> ); }; export default createWidget('MyWidget', MyWidget);
小组件名称('MyWidget')必须与 app 配置中小组件配置里的 name 字段匹配。
基础小组件
更新小组件的有效方式之一是使用 updateSnapshot 方法。它会创建一个仅包含单个条目的小组件时间线,并立即显示。
The example below continues from Creating a widget.
import MyWidget from './MyWidget'; // 更新小组件 MyWidget.updateSnapshot({ count: 5 });
时间线小组件
Use the updateTimeline method to schedule widget updates at a specific time. The system automatically updates the widget based on the timeline.
The example below continues from Creating a widget.
import MyWidget from './MyWidget'; MyWidget.updateTimeline([ { date: new Date(), props: { count: 1 } }, { date: new Date(Date.now() + 3600000), props: { count: 2 } }, // 距现在 1 小时 { date: new Date(Date.now() + 7200000), props: { count: 3 } }, // 距现在 2 小时 { date: new Date(Date.now() + 10800000), props: { count: 4 } }, // 距现在 3 小时 ]);
Reading the current timeline
Use getTimeline to read the entries currently scheduled for a widget, including past and future entries.
import MyWidget from './MyWidget'; const entries = await MyWidget.getTimeline(); // [{ date: Date, props: { count: number } }, ...]
Reloading a widget
Use reload to force the system to refresh a widget's content and timeline immediately, for example after the underlying data changes.
import MyWidget from './MyWidget'; MyWidget.reload();
Responsive widget
使用 environment 参数可以根据当前小组件大小和渲染上下文调整布局。
import { HStack, Text, VStack } from '@expo/ui/swift-ui'; import { createWidget, type WidgetEnvironment } from 'expo-widgets'; type WeatherWidgetProps = { temperature: number; condition: string; }; const WeatherWidget = (props: WeatherWidgetProps, environment: WidgetEnvironment) => { 'widget'; // 根据大小渲染不同布局 if (environment.widgetFamily === 'systemSmall') { return ( <VStack> <Text>{props.temperature}°</Text> </VStack> ); } if (environment.widgetFamily === 'systemMedium') { return ( <HStack> <Text>{props.temperature}°</Text> <Text>{props.condition}</Text> </HStack> ); } // systemLarge 和其他 return ( <VStack> <Text>温度: {props.temperature}°</Text> <Text>天气: {props.condition}</Text> <Text>更新于: {environment.date.toLocaleTimeString()}</Text> </VStack> ); }; const Widget = createWidget('WeatherWidget', WeatherWidget); export default Widget; Widget.updateSnapshot({ temperature: 72, condition: 'Sunny', });
Adapting to the rendering environment
Beyond widgetFamily and date, the environment object describes how and where the system is drawing the widget so you can adapt the layout:
colorScheme:'light'or'dark'.widgetRenderingMode:'fullColor'for home screen widgets,'vibrant'for Lock Screen widgets (the system desaturates them into an adaptive monochrome look), and'accented'for tinted widgets on iOS 18 and later. Use this to choose colors that read well in each mode.isLuminanceReduced:truewhen the display requires reduced brightness (such as Always-On). Lower the overall brightness of your content, for example by using stroked rather than filled shapes.widgetContentMargins: The system-suggested margins (top,bottom,leading,trailing) when content margins are not disabled.showsWidgetLabel: For accessory widgets, whether an accessory label can be displayed.
Interactive widgets
Widgets can include interactive controls such as Button. The value a button's onPress callback returns becomes the widget's new props. The runtime persists it and reloads the widget on device, with no running app process required. This is the primary way to make a widget update itself in response to a tap. Interactive widgets require iOS 17 or later.
import { Button, Text, VStack } from '@expo/ui/swift-ui'; import { createWidget } from 'expo-widgets'; type CounterProps = { count: number; }; const CounterWidget = (props: CounterProps) => { 'widget'; return ( <VStack> <Text>Count: {props.count}</Text> <Button label="Increment" target="increment" onPress={() => ({ count: props.count + 1 })} /> </VStack> ); }; export default createWidget('CounterWidget', CounterWidget);
To also keep your running app in sync with widget interactions, give the control a target identifier (as above) and listen for taps with addUserInteractionListener. The listener receives the widget's name as source and the control's target. Unlike onPress, it only fires while the app process is alive, so use it to mirror interactions into app state, not as the widget's update mechanism.
import { addUserInteractionListener } from 'expo-widgets'; const subscription = addUserInteractionListener(event => { if (event.source === 'CounterWidget' && event.target === 'increment') { // The widget already updated itself via onPress; mirror the change in app state here. console.log('Counter incremented from the widget'); } }); // Later, when you no longer need updates: subscription.remove();
Sharing images with widgetsDirectory
A widget can't access files inside your app's sandbox, so to display an image in a widget you must place it in the shared app group container. widgetsDirectory is a file:// URL string pointing to a directory that both your app and its widgets can read. Write the image there from your app, then reference it by path in the widget.
import { widgetsDirectory } from 'expo-widgets'; // `widgetsDirectory` is a file:// URL to a directory shared with your widgets. console.log(widgetsDirectory);
widgetsDirectoryisnullonly when no app group is configured. ThegroupIdentifierconfig plugin option sets one up automatically (falling back togroup.<bundle identifier>), so it is available in normal use.
Configurable widgets
When you add an ios.configuration to a widget, users can long-press the widget and edit its parameters. The values they choose are delivered to your widget through environment.configuration. Type the configuration by passing a second type argument to createWidget (and WidgetEnvironment). Configurable widgets require iOS 17 or later.
import { Text, VStack } from '@expo/ui/swift-ui'; import { createWidget, type WidgetEnvironment } from 'expo-widgets'; type WeatherProps = { temperature: number; }; type WeatherConfiguration = { city: string; }; const WeatherWidget = ( props: WeatherProps, environment: WidgetEnvironment<WeatherConfiguration> ) => { 'widget'; return ( <VStack> <Text>{environment.configuration.city}</Text> <Text>{props.temperature}°</Text> </VStack> ); }; export default createWidget<WeatherProps, WeatherConfiguration>('WeatherWidget', WeatherWidget);
Live Activities
Live Activities 会在受支持的设备上于锁屏和灵动岛中显示实时信息。
Prerequisite: Creating a Live Activity
Live Activity layouts must be created once using createLiveActivity and marked with the 'widget' directive. The component receives your props as the first argument and a LiveActivityEnvironment object as the second. It returns an object describing the layout for each presentation: the Lock Screen banner, the compact and minimal Dynamic Island states, and the expanded Dynamic Island regions.
createLiveActivityregisters a Live Activity entirely at runtime, and the library's built-in Live Activity target renders it. Do not add awidgets[]entry for it in the app config. Thewidgets[]array is only for home screen and Lock Screen widgets, and an entry withoutsupportedFamiliesgenerates an invalid widget target that fails to build. Thenameyou pass tocreateLiveActivityonly has to match thiscreateLiveActivitycall, not an app-config widget.
import { Image, Text, VStack } from '@expo/ui/swift-ui'; import { font, foregroundStyle, padding } from '@expo/ui/swift-ui/modifiers'; import { createLiveActivity, type LiveActivityEnvironment } from 'expo-widgets'; type DeliveryActivityProps = { etaMinutes: number; status: string; }; const DeliveryActivity = (props: DeliveryActivityProps, environment: LiveActivityEnvironment) => { 'widget'; const accentColor = environment.isLuminanceReduced ? '#FFFFFF' : '#007AFF'; return { banner: ( <VStack modifiers={[padding({ all: 12 })]}> <Text modifiers={[font({ weight: 'bold' }), foregroundStyle(accentColor)]}> {props.status} </Text> <Text>预计送达:{props.etaMinutes} 分钟</Text> </VStack> ), compactLeading: <Image systemName="box.truck.fill" color={accentColor} />, compactTrailing: <Text>{props.etaMinutes} 分钟</Text>, minimal: <Image systemName="box.truck.fill" color={accentColor} />, expandedLeading: ( <VStack modifiers={[padding({ all: 12 })]}> <Image systemName="box.truck.fill" color={accentColor} /> <Text modifiers={[font({ size: 12 })]}>正在配送</Text> </VStack> ), expandedTrailing: ( <VStack modifiers={[padding({ all: 12 })]}> <Text modifiers={[font({ weight: 'bold', size: 20 })]}>{props.etaMinutes}</Text> <Text modifiers={[font({ size: 12 })]}>分钟</Text> </VStack> ), expandedBottom: ( <VStack modifiers={[padding({ all: 12 })]}> <Text>司机:John Smith</Text> <Text>订单 #12345</Text> </VStack> ), }; }; export default createLiveActivity('DeliveryActivity', DeliveryActivity);
The layout object supports these regions:
banner: The main Lock Screen presentation.bannerSmall: A compact Lock Screen presentation used on CarPlay and watchOS. Falls back tobannerwhen omitted.compactLeading,compactTrailing,minimal: The compact and minimal Dynamic Island states.expandedLeading,expandedTrailing,expandedCenter,expandedBottom: The regions of the expanded Dynamic Island.
The environment object also exposes isLuminanceReduced, isActivityFullscreen, isActivityUpdateReduced, and activityFamily so you can adapt the layout to the current presentation.
Starting a Live Activity
The example below continues from Creating a Live Activity.
import { Button, View } from 'react-native'; import DeliveryActivity from './DeliveryActivity'; function App() { const startDeliveryTracking = () => { // 启动 Live Activity const instance = DeliveryActivity.start( { etaMinutes: 15, status: 'Your delivery is on the way', }, 'myapp://deliveries/12345' ); // 存储实例 }; return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Button title="Start Delivery Tracking" onPress={startDeliveryTracking} /> </View> ); } export default App;
The optional second argument is a URL associated with the Live Activity. When the user taps the activity, the system opens your app with that URL, so you can route to the relevant screen using linking (for example with Expo Router's deep linking).
Updating a Live Activity
下面的示例接续自启动 Live Activity。
import { LiveActivity } from 'expo-widgets'; function updateDelivery(instance: LiveActivity<DeliveryActivityProps>) { instance.update({ etaMinutes: 2, status: 'Delivery arriving soon!', }); }
Recovering active Live Activities
A Live Activity can outlive the app process that started it. Use getInstances on the factory to retrieve the activities of that type that are currently active, for example to update or end them after your app relaunches.
import DeliveryActivity from './DeliveryActivity'; const activeInstances = DeliveryActivity.getInstances(); for (const instance of activeInstances) { await instance.update({ etaMinutes: 5, status: 'Almost there' }); }
Ending a Live Activity
使用 end 来结束 Live Activity。你可以选择关闭策略,可选地提供最终内容状态,并传入 contentDate,以便系统忽略过时更新。
import { after, type LiveActivity } from 'expo-widgets'; async function completeDelivery(instance: LiveActivity<DeliveryActivityProps>) { await instance.end( after(new Date(Date.now() + 15 * 60 * 1000)), { etaMinutes: 0, status: 'Delivered', }, new Date() ); }
你也可以在关闭策略中使用 'default' 或 'immediate' 来代替 after(date)。
Remote updates with push notifications
When enablePushNotifications is true, you can update Live Activities remotely from your server through Apple Push Notification service (APNs).
- Use
addPushToStartTokenListenerto receive the app-wide push-to-start token, which lets your server start a Live Activity remotely (requires iOS 17.2 or later). - Use
instance.getPushToken()orinstance.addPushTokenListener()to obtain the token for a specific running Live Activity, which lets your server send updates to that activity.
import { addPushToStartTokenListener } from 'expo-widgets'; import DeliveryActivity from './DeliveryActivity'; const pushToStartSubscription = addPushToStartTokenListener(event => { console.log('Push-to-start token:', event.activityPushToStartToken); }); async function startDeliveryTracking() { const instance = DeliveryActivity.start({ etaMinutes: 15, status: 'Your delivery is on the way', }); const pushToken = await instance.getPushToken(); console.log('Per-activity token:', pushToken); const subscription = instance.addPushTokenListener(event => { console.log('Updated push token:', event.activityId, event.pushToken); }); // 以后,当你不再需要更新时: subscription.remove(); } // 以后,当你不再需要更新时: pushToStartSubscription.remove();
Send the token to your server and use it to push updates. The notification must use the liveactivity push type (the apns-push-type header) and an apns-topic of <your bundle identifier>.push-type.liveactivity. Its aps payload carries an event (start, update, or end), a timestamp, and a content-state that matches your activity's props. The content-state must match the internal content state used by expo-widgets: set name to the name you passed to createLiveActivity, and set props to a JSON string of that activity's props. Use apns-priority: 10 for immediate updates and apns-priority: 5 for lower-priority updates. The timestamp, dismissal-date, and other APNs date fields are Unix timestamps in seconds.
To start a Live Activity remotely, send a start event to the push-to-start token:
{ "aps": { "timestamp": 1778832000, "event": "start", "attributes-type": "LiveActivityAttributes", "attributes": {}, "content-state": { "name": "DeliveryActivity", "props": "{\"etaMinutes\":15,\"status\":\"Your delivery is on the way\"}" }, "alert": { "title": "Delivery started", "body": "Your delivery is on the way" } } }
Remote start requires iOS 17.2 or later. Include input-push-token: 1 in the aps payload on iOS 18 or later if you want APNs to provide a new per-activity token for future updates.
To update a Live Activity remotely, send an update event to that activity's per-activity token:
{ "aps": { "timestamp": 1778832300, "event": "update", "content-state": { "name": "DeliveryActivity", "props": "{\"etaMinutes\":2,\"status\":\"Delivery arriving soon!\"}" } } }
To end a Live Activity remotely, send an end event with the final content state:
{ "aps": { "timestamp": 1778832600, "event": "end", "content-state": { "name": "DeliveryActivity", "props": "{\"etaMinutes\":0,\"status\":\"Delivered\"}" }, "dismissal-date": 1778833200 } }
For the exact payload shape and headers, follow Apple's Starting and updating Live Activities with ActivityKit push notifications.
API
import { createWidget, createLiveActivity } from 'expo-widgets';
Classes
Represents a Live Activity instance. Provides methods to update its content and end it.
LiveActivity Methods
| Parameter | Type | Description |
|---|---|---|
| listener | (event: PushTokenEvent) => void | Callback invoked when a new push token is available. |
Adds a listener for push token update events on this Live Activity instance. The token can be used to send content updates to this specific activity via APNs.
EventSubscriptionAn event subscription that can be used to remove the listener.
| Parameter | Type | Description |
|---|---|---|
| dismissalPolicy(optional) | LiveActivityDismissalPolicy | Controls when the Live Activity is removed from the Lock Screen after ending.
Can be |
| props(optional) | T | Final content properties to update after the activity ends. |
| contentDate(optional) | Date | The time the data in the payload was generated. If this is older than a previous update or push payload, the system ignores this update. |
Ends the Live Activity.
Promise<void>Returns the push token for this Live Activity, used to send push notification updates via APNs.
Returns null if push notifications are not enabled or the token is not yet available.
Promise<string | null>| Parameter | Type | Description |
|---|---|---|
| props | T | The updated content properties. |
Updates the Live Activity's content. The UI reflects the new properties immediately.
Promise<void>Manages Live Activity instances of a specific type. Use it to start new activities and retrieve currently active ones.
LiveActivityFactory Methods
Returns all currently active instances of this Live Activity type.
LiveActivity[]| Parameter | Type | Description |
|---|---|---|
| props | T | The initial content properties for the Live Activity. |
| url(optional) | string | An optional URL to associate with the Live Activity, used for deep linking. |
Starts a new Live Activity with the given properties.
LiveActivity<T>The new Live Activity instance.
Represents a widget instance. Provides methods to manage the widget's timeline.
Widget Methods
Returns the current timeline entries for the widget, including past and future entries.
Promise<WidgetTimelineEntry[]>| Parameter | Type | Description |
|---|---|---|
| props | T | The properties to display in the widget. |
Sets the widget's content to the given props immediately, without scheduling a timeline.
void| Parameter | Type | Description |
|---|---|---|
| entries | WidgetTimelineEntry[] | Timeline entries, each specifying a date and the props to display at that time. |
Schedules a series of updates for the widget's content and reloads the widget.
voidMethods
| Parameter | Type | Description |
|---|---|---|
| name | string | The Live Activity name. Must match the |
| liveActivity | LiveActivityComponent<T> | The Live Activity component, marked with the |
Creates a Live Activity Factory for managing Live Activities of a specific type.
LiveActivityFactory<T>| Parameter | Type | Description |
|---|---|---|
| name | string | The widget name. Must match the |
| widget | (props: T, context: WidgetEnvironment) => Element | The widget component, marked with the |
Creates a Widget instance.
Widget<T>Event subscriptions
| Parameter | Type | Description |
|---|---|---|
| listener | (event: PushToStartTokenEvent) => void | Callback function to handle push-to-start token events. |
Adds a listener for push-to-start token events. This token can be used to start live activities remotely via APNs.
EventSubscriptionAn event subscription that can be used to remove the listener.
| Parameter | Type | Description |
|---|---|---|
| listener | (event: UserInteractionEvent) => void | Callback function to handle user interaction events. |
Adds a listener for widget interaction events (for example, button taps).
EventSubscriptionAn event subscription that can be used to remove the listener.
Types
| Property | Type | Description |
|---|---|---|
| onExpoWidgetsPushToStartTokenReceived | (event: PushToStartTokenEvent) => void | Function that is invoked when a push-to-start token is received. event: PushToStartTokenEventToken event details. |
| onExpoWidgetsUserInteraction | (event: UserInteractionEvent) => void | Function that is invoked when user interacts with a widget. event: UserInteractionEventInteraction event details. |
Literal type: string
The level of detail the view is recommended to have. The system can update the levelOfDetail value based on user proximity or other system specific factors and allow content customization adapting to show different levels of details.
simplified— The system recommends showing a simplified view with less details.default— The system has no specific recommendation for the level of detail.
Acceptable values are: 'simplified' | 'default'
A function that returns the layout for a Live Activity.
| Parameter | Type |
|---|---|
| props | T |
| environment | LiveActivityEnvironment |
Literal type: union
Dismissal policy for ending a live activity.
'default'- The system’s default dismissal policy for the Live Activity.'immediate'- The system immediately removes the Live Activity that ended.after(date)- The system removes the Live Activity that ended at the specified time within a four-hour window.
Acceptable values are: 'default' | 'immediate' | ReturnType<after>
| Property | Type | Description |
|---|---|---|
| activityFamily(optional) | ActivityFamily | Only for: iOS 18+ The size family of the current Live Activity. |
| colorScheme | 'light' | 'dark' | The color scheme of the activity's environment. |
| isActivityFullscreen(optional) | boolean | Only for: iOS 16.1+ Whether the activity is currently displayed in fullscreen. |
| isActivityUpdateReduced(optional) | boolean | Only for: iOS 18+ A Boolean value that indicates whether the Live Activity update synchronization rate is reduced. |
| isLuminanceReduced(optional) | boolean | Only for: iOS 16+ Whether the activity is displayed in a context with reduced luminance. |
| levelOfDetail(optional) | LevelOfDetail | Only for: iOS 26+ The level of detail the view is recommended to have. |
| Property | Type | Description |
|---|---|---|
| onExpoWidgetsTokenReceived | (event: PushTokenEvent) => void | Function that is invoked when a push token is received for a live activity. event: PushTokenEventToken event details. |
Defines the layout sections for an iOS Live Activity.
| Property | Type | Description |
|---|---|---|
| banner | ReactNode | The main banner content displayed in Notifications Center. |
| bannerSmall(optional) | ReactNode | The small banner content displayed in CarPlay and WatchOS. Falls back to |
| compactLeading(optional) | ReactNode | The leading content in the compact Dynamic Island presentation. |
| compactTrailing(optional) | ReactNode | The trailing content in the compact Dynamic Island presentation. |
| expandedBottom(optional) | ReactNode | The bottom content in the expanded Dynamic Island presentation. |
| expandedCenter(optional) | ReactNode | The center content in the expanded Dynamic Island presentation. |
| expandedLeading(optional) | ReactNode | The leading content in the expanded Dynamic Island presentation. |
| expandedTrailing(optional) | ReactNode | The trailing content in the expanded Dynamic Island presentation. |
| minimal(optional) | ReactNode | The minimal content shown when the Dynamic Island is in its smallest form. |
Event emitted when a push token is received for a live activity.
| Property | Type | Description |
|---|---|---|
| activityId | string | The ID of the live activity. |
| pushToken | string | The push token for the live activity. |
Event emitted when a push-to-start token is received.
| Property | Type | Description |
|---|---|---|
| activityPushToStartToken | string | The push-to-start token for starting live activities remotely. |
Event emitted when a user interacts with a widget.
| Property | Type | Description |
|---|---|---|
| source | string | Widget that triggered the interaction. |
| target | string | Button/toggle that was pressed. |
| timestamp | number | Timestamp of the event. |
| type | 'ExpoWidgetsUserInteraction' | The event type identifier. |
| Property | Type | Description |
|---|---|---|
| colorScheme(optional) | 'light' | 'dark' | The color scheme of the widget's environment. |
| date | Date | The date of this timeline entry. |
| isLuminanceReduced(optional) | boolean | Only for: iOS 16+ A Boolean value that indicates whether the display or environment currently requires reduced luminance. When you detect this condition, lower the overall brightness of your view. For example, you can change large, filled shapes to be stroked, and choose less bright colors. |
| levelOfDetail(optional) | LevelOfDetail | Only for: iOS 26+ The level of detail the view is recommended to have. |
| showsWidgetLabel(optional) | boolean | Only for: iOS 16+ A Boolean value that indicates whether an accessory family widget can display an accessory label. |
| widgetContentMargins(optional) | {
bottom: number,
leading: number,
top: number,
trailing: number
} | Only for: iOS 17+ The content margins for the widget. |
| widgetFamily | WidgetFamily | The widget family. |
| widgetRenderingMode(optional) | WidgetRenderingMode | Only for: iOS 16+ The widget's rendering mode, based on where the system is displaying it. |
Literal type: string
The widget family (size).
systemSmall- Small square widget (2x2 grid).systemMedium- Medium widget (4x2 grid).systemLarge- Large widget (4x4 grid).systemExtraLarge- Extra large widget (iPad only, 6x4 grid).accessoryCircular- Circular accessory widget for the Lock Screen.accessoryRectangular- Rectangular accessory widget for the Lock Screen.accessoryInline- Inline accessory widget for the Lock Screen.
Acceptable values are: 'systemSmall' | 'systemMedium' | 'systemLarge' | 'systemExtraLarge' | 'accessoryCircular' | 'accessoryRectangular' | 'accessoryInline'
Literal type: string
The rendering mode of the widget as provided by WidgetKit.
fullColor— Home screen widgets (default).accented— Tinted widgets (iOS 18+) and watchOS.vibrant— Lock screen widgets.
Acceptable values are: 'fullColor' | 'accented' | 'vibrant'
| Property | Type | Description |
|---|---|---|
| date | Date | Date when widget should update. |
| props | T | Props to be passed to the widget. |