Configurable Widgets
Configurable widgets let users edit widget parameters in the native iOS Edit Widget sheet. Use them when a Dynamic Widget needs a few user-editable knobs, such as a label, unit, theme, or source.
It requires iOS 17+, because Voltra wires it through AppIntentConfiguration.
How it works
Section titled “How it works”- Define a Dynamic Widget module with a default export.
- Add
entryandappIntent.parametersto the widget config inapp.json. - Read the selected values from
env.configurationin your widget’s JSX. - Build and install the app on a real iPhone, add the widget to the Home Screen, then long-press it and tap Edit Widget to change parameters — your widget re-reads
env.configurationwith the new values.
Each parameter has:
name: key that appears inenv.configurationtitle: label shown in the Edit Widget sheet, as a plain string or a locale mapdefault: code-defined starting value before the user changes anythingoptions(optional): fixed values to pick from, each with avalueand atitle. The sheet shows a picker with the titles instead of a free-text field, andenv.configurationreceives thevalue.defaultmust be one of the values; without adefault, the first option is used.
How to use it
Section titled “How to use it”import { Voltra, type WidgetEnvironment } from '@use-voltra/ios'
type GreetingConfig = { label?: string }
export default function GreetingWidget( _props: object, env: WidgetEnvironment<GreetingConfig> = {} as WidgetEnvironment<GreetingConfig>) {
const label = env.configuration?.label ?? 'Hello'
return ( <Voltra.VStack style={{ padding: 16, backgroundColor: '#0F172A' }}> <Voltra.Text style={{ color: 'white', fontSize: 18, fontWeight: '700' }}> {label} </Voltra.Text> <Voltra.Text style={{ color: '#94A3B8', marginTop: 6 }}> Edit me from the widget sheet. </Voltra.Text> </Voltra.VStack> )}Plugin config:
{ "expo": { "plugins": [ [ "@use-voltra/ios-client", { "widgets": [ { "id": "greeting_widget", "entry": "./widgets/ios/greeting-widget.tsx", "displayName": "Greeting Widget", "description": "A Dynamic Widget with user-editable parameters", "supportedFamilies": ["systemSmall", "systemMedium"], "initialStatePath": "./widgets/ios/greeting-widget.tsx", "appIntent": { "parameters": [ { "name": "label", "title": "Label", "default": "Hello" } ] } } ] } ] ] }}If you need more than one value, add more entries to appIntent.parameters and read each key from env.configuration.
Offer fixed choices
Section titled “Offer fixed choices”When a parameter has a known set of values, list them as options. The sheet then shows a picker:
{ "name": "units", "title": "Units", "default": "metric", "options": [ { "value": "metric", "title": "Metric" }, { "value": "imperial", "title": "Imperial" } ]}env.configuration.units is "metric" or "imperial", whichever the user picked.
Expo Prebuild and Voltra Apply stop with an error that names the parameter if default is not one of the values, a value is empty or listed twice, or an option has no title.
Translate the Edit Widget sheet
Section titled “Translate the Edit Widget sheet”iOS draws the Edit Widget sheet itself, so its text comes from app.json rather than from your widget code. title and options[].title accept locale maps, the same way displayName does. The sheet header shows the widget’s displayName, so translate it the same way:
{ "id": "weather_widget", "entry": "./widgets/ios/weather-widget.tsx", "displayName": { "en": "Weather", "pl": "Pogoda" }, "description": { "en": "Current conditions", "pl": "Aktualne warunki" }, "appIntent": { "parameters": [ { "name": "units", "title": { "en": "Units", "pl": "Jednostki" }, "default": "metric", "options": [ { "value": "metric", "title": { "en": "Metric", "pl": "Metryczne" } }, { "value": "imperial", "title": { "en": "Imperial", "pl": "Imperialne" } } ] } ] }}iOS shows the sheet in the system language, and setDynamicWidgetLocale does not affect it. A language with no translation for a string shows your app’s development language instead. Rebuild the native app after changing these. See Localization for how the app’s languages are declared.
appIntentonly wires up for Dynamic Widgets.- Defaults come from code, not from the native sheet.
- There is no
exportfield in app.json for Dynamic Widgets. - Use a real device to verify the Edit Widget flow.