Plugin configuration
The Voltra Expo config plugin accepts several configuration options in your app.json or app.config.js:
{ "expo": { "plugins": [ [ "@use-voltra/ios-client", { "groupIdentifier": "group.your.bundle.identifier", "enablePushNotifications": true, "deploymentTarget": "18.0", "targetName": "MyAppLiveActivity", "widgets": [ { "id": "weather", "displayName": "Weather Widget", "description": "Shows current weather conditions", "supportedFamilies": ["systemSmall", "systemMedium", "systemLarge"], "initialStatePath": "./widgets/weather-initial.tsx" } ] } ] ] }}Configuration options
Section titled “Configuration options”groupIdentifier (optional)
Section titled “groupIdentifier (optional)”App Group identifier for sharing data between your app and the widget extension. Required if you want to:
- Forward component events (like button taps) from Live Activities to your JavaScript code
- Share images between your app and the extension
- Use image preloading features
- Update entry-based Dynamic Widgets with runtime props
- Use Dynamic Live Activities
Format: Must start with group. (e.g., group.your.bundle.identifier)
enablePushNotifications (optional)
Section titled “enablePushNotifications (optional)”Enable server-side updates for Live Activities via Apple Push Notification Service (APNS). When enabled, you can update Live Activities even when your app is in the background or terminated.
Type: boolean
Default: false
liveActivities (optional)
Section titled “liveActivities (optional)”Bundled Dynamic Live Activity definitions. Every declaration has a stable id and an entry module that default-exports the renderer function. IDs use only alphanumeric characters and underscores, and are unique within this collection; they are separate from Dynamic Widget IDs.
When this array is non-empty, groupIdentifier is required. See Dynamic Live Activities for the entry signature, lifecycle APIs, and push payload contract.
deploymentTarget (optional)
Section titled “deploymentTarget (optional)”iOS deployment target version for the widget extension. If not provided, defaults to 17.0. This allows the widget extension to have its own deployment target independent of the main app.
Type: string
Default: "17.0"
Example: "18.0"
Note: Code signing settings (development team, provisioning profiles) are automatically synchronized from the main app target, but the deployment target can be set independently.
targetName (optional)
Section titled “targetName (optional)”Custom target name for the widget extension. If not provided, defaults to {AppName}LiveActivity where AppName is your app’s sanitized name.
Useful for matching existing provisioning profiles, credentials, or naming conventions (e.g., when migrating from @bacons/apple-targets).
Type: string
Default: "{AppName}LiveActivity"
Example: "widget", "MyAppLiveActivity"
{ "expo": { "plugins": [ [ "@use-voltra/ios-client", { "groupIdentifier": "group.your.bundle.identifier", "targetName": "widget" } ] ] }}widgets (optional)
Section titled “widgets (optional)”Array of widget configurations for Home Screen widgets. Each widget will be available in the iOS widget gallery.
Widget Configuration Properties:
id: Unique identifier for the widget (alphanumeric with underscores only)displayName: Name shown in the widget gallery (plain string, or per-locale map like{ "en": "Weather", "pl": "Pogoda" }; locale keys are BCP‑47-style tags)description: Description shown in the widget gallery (same localization rules asdisplayName)kind(optional): WidgetKit kind of the widget. Defaults toVoltra_Widget_<id>; see Keeping the kind of an existing widgetsupportedFamilies: Array of supported widget sizes (systemSmall,systemMedium,systemLarge)initialStatePath: (optional) Project-relative path to a file that exports initial widget state, or a locale map of paths for localized build-time pre-rendering (see Widget Pre-rendering)serverUpdate: (optional) Fetch the widget’s content on a schedule. Withoutentrythe server returns a rendered payload; withentryit returns plain JSON that the widget renders on the device.urlis optional — leave it out to supply one at runtime. See Server-driven widgets for full details.url: The Voltra SSR endpoint URLintervalMinutes: Update interval in minutes (default:15)refresh: Show a native refresh button (default:false, requires iOS 17+)
appIntent: (optional, Dynamic Widgets, iOS 17+) Parameters the user edits in the Edit Widget sheet. Each parameter has aname, atitle(plain string or locale map), an optionaldefault, and optionaloptions([{ "value": "metric", "title": "Metric" }]) that turn the field into a picker. See Configurable Widgets
Example:
{ "widgets": [ { "id": "weather", "displayName": "Weather Widget", "description": "Current weather conditions", "supportedFamilies": ["systemSmall", "systemMedium", "systemLarge"], "initialStatePath": { "en": "./widgets/weather-initial.tsx", "pl": "./widgets/weather-initial-pl.tsx" }, "serverUpdate": { "url": "https://api.example.com/widgets/render", "intervalMinutes": 30, "refresh": true } } ]}Keeping the kind of an existing widget
Section titled “Keeping the kind of an existing widget”WidgetKit identifies a widget placed on the Home Screen by its extension bundle identifier and its kind. When you migrate an existing WidgetKit widget to Voltra, keep both so users don’t have to remove and re-add the widget: set targetName to the name of your existing extension target and kind to the kind your WidgetConfiguration used.
{ "targetName": "MyWidgets", "widgets": [ { "id": "streak", "kind": "StreakWidget", "displayName": "Streak", "description": "Your daily streak" } ]}The id stays the handle you use from JavaScript (updateWidget('streak', ...)), only the WidgetKit kind changes. Kinds must be unique across widgets. This works the same way in voltra.config.ts when you use the React Native CLI setup.
Localizing displayName and description
Section titled “Localizing displayName and description”Use a locale map when the widget gallery label should be translated:
{ "widgets": [ { "id": "weather", "displayName": { "en": "Weather", "pl": "Pogoda", "zh-Hans": "天气" }, "description": { "en": "Current weather conditions", "pl": "Aktualne warunki pogodowe", "zh-Hans": "当前天气状况" } } ]}Use BCP-47-style locale tags such as en, en-US, pt-BR, or zh-Hans.
Fallback behavior:
- Voltra first tries the device locale.
- If there is no exact match, it falls back to the language-only match.
- If there is still no match, it prefers an English locale such as
enoren-US. - If no English entry exists, it uses the first configured locale.
Languages widgets can use
Section titled “Languages widgets can use”Widgets declare the same languages as the app, so env.locale resolves to the same language the app runs in. The list comes from the Expo locales config keys, ios.infoPlist.CFBundleLocalizations, every locale used in a widget locale map, and the app’s development language (ios.infoPlist.CFBundleDevelopmentRegion, otherwise en). A language missing from this list is never used for env.locale, even when the user prefers it. See Localization.