Problem
Voltra's current model resolves everything — colors, sizes, content — on the server into a static
JSON snapshot. The native side is a pure interpreter: it receives the snapshot and displays it.
SwiftUI and Glance both have reactive environment systems, but Voltra bypasses them entirely.
This means the following on-device state changes cannot be reflected in widgets without a new
server push:
- System appearance (dark/light mode, high contrast, reduced motion)
- Dynamic Type scale
- iOS
widgetRenderingMode (fullColor / accented / vibrant) — required for Liquid Glass and accented rendering
- Android Material You dynamic color tokens
- User-configurable widget parameters (iOS
AppIntentConfiguration on iOS 17+; Android Glance config activities)
- Time-based changes
The user-configurable parameter case is the hardest gap: when a user configures a widget in the
gallery, there is no server push — the widget process must render locally with the new parameter.
Today, Voltra has no mechanism for this on either platform.
Goal
Enable Voltra widgets to re-render in response to on-device state changes — primarily
user-configured parameters — without a server push and without an app update, while keeping the
RN-only developer experience (no Swift or Kotlin required from the developer).
Constraints
- Server-driven layout must remain intact where possible — it is Voltra's core value proposition
- Developer API stays in JSX +
app.json; config plugin generates all platform-native boilerplate
- iOS-specific: no new linked dependencies in the widget extension (extension memory budget is ~30 MB; JSC is preferred because it is a system framework with zero binary cost)
- Android-specific: reuse Hermes if possible (already linked into every RN-on-Android app — zero added binary cost)
Context
The current server-driven JSON model has no mechanism for AppIntent / configurable-widget support
on either platform: when a user changes parameters, there is no server push, so the renderer has
nothing to apply the new values to. Each platform has its own architecture, so the approaches
explored on each are different — but the shared underlying claim is the same: some form of
on-device dynamic-value resolution is required, and we want to validate alternatives to natively-
interpreted opcode tuples (#130) and server-round-trip configurable widgets (#147).
Approaches explored — iOS
JSC-in-Extension (Track 2) — a thin JS resolver runs in the widget extension at render time.
Resolves {{ appIntent.X }} template expressions against current AppIntent parameters, then feeds
the resolved JSON into the existing Swift interpreter. Server retains layout control. Uses
JavaScriptCore (system framework — zero binary cost, no new linked dependency).
Build-Time Codegen (Track 3) — the Voltra component is compiled to self-contained SwiftUI at
build time by the config plugin. Full native reactivity, zero runtime overhead. Server can push
data props but not layout changes — an app update is required for structural changes.
A complementary iOS branch (Track 1, poc/widget-reactivity-track-1) addresses rendering
improvements that are independent of AppIntent reactivity: widgetAccentable prop support and
light-dark() CSS Color Level 4 syntax for adaptive text colors. It is a standalone concern that
can be merged separately and is not a prerequisite for either of the above approaches.
Approaches explored — Android
Hermes-in-Process (Track 4) — the same JS-resolver pattern as iOS Track 2, but adapted to
Android's architecture. Unlike iOS widget extensions, Glance widget updates run inside the main
app process via WorkManager — there is no sandboxed extension to slip a JS engine into. Instead,
Voltra instantiates a standalone Hermes runtime via a custom JNI/NDK wrapper, invoked from
VoltraGlanceWidget.provideGlance(). The shared JS bundle (same source as iOS Track 2) resolves
{{ appIntent.X }} placeholders against parameters stored in DataStore, populated by an in-app
UI (the PoC's stand-in for a future Glance configuration activity).
The architectural claim: one JS resolver bundle, two platforms, with strictly more expressiveness
than opcode tuples. PoC scope is narrow — prove engine integration and the reactivity loop. Does
not (yet) replace the native opcode-tuple resolver from #130 or add Glance configuration
activities (analog of #147).
Related
Problem
Voltra's current model resolves everything — colors, sizes, content — on the server into a static
JSON snapshot. The native side is a pure interpreter: it receives the snapshot and displays it.
SwiftUI and Glance both have reactive environment systems, but Voltra bypasses them entirely.
This means the following on-device state changes cannot be reflected in widgets without a new
server push:
widgetRenderingMode(fullColor / accented / vibrant) — required for Liquid Glass and accented renderingAppIntentConfigurationon iOS 17+; Android Glance config activities)The user-configurable parameter case is the hardest gap: when a user configures a widget in the
gallery, there is no server push — the widget process must render locally with the new parameter.
Today, Voltra has no mechanism for this on either platform.
Goal
Enable Voltra widgets to re-render in response to on-device state changes — primarily
user-configured parameters — without a server push and without an app update, while keeping the
RN-only developer experience (no Swift or Kotlin required from the developer).
Constraints
app.json; config plugin generates all platform-native boilerplateContext
The current server-driven JSON model has no mechanism for AppIntent / configurable-widget support
on either platform: when a user changes parameters, there is no server push, so the renderer has
nothing to apply the new values to. Each platform has its own architecture, so the approaches
explored on each are different — but the shared underlying claim is the same: some form of
on-device dynamic-value resolution is required, and we want to validate alternatives to natively-
interpreted opcode tuples (#130) and server-round-trip configurable widgets (#147).
Approaches explored — iOS
JSC-in-Extension (Track 2) — a thin JS resolver runs in the widget extension at render time.
Resolves
{{ appIntent.X }}template expressions against current AppIntent parameters, then feedsthe resolved JSON into the existing Swift interpreter. Server retains layout control. Uses
JavaScriptCore (system framework — zero binary cost, no new linked dependency).
Build-Time Codegen (Track 3) — the Voltra component is compiled to self-contained SwiftUI at
build time by the config plugin. Full native reactivity, zero runtime overhead. Server can push
data props but not layout changes — an app update is required for structural changes.
A complementary iOS branch (Track 1,
poc/widget-reactivity-track-1) addresses renderingimprovements that are independent of AppIntent reactivity:
widgetAccentableprop support andlight-dark()CSS Color Level 4 syntax for adaptive text colors. It is a standalone concern thatcan be merged separately and is not a prerequisite for either of the above approaches.
Approaches explored — Android
Hermes-in-Process (Track 4) — the same JS-resolver pattern as iOS Track 2, but adapted to
Android's architecture. Unlike iOS widget extensions, Glance widget updates run inside the main
app process via WorkManager — there is no sandboxed extension to slip a JS engine into. Instead,
Voltra instantiates a standalone Hermes runtime via a custom JNI/NDK wrapper, invoked from
VoltraGlanceWidget.provideGlance(). The shared JS bundle (same source as iOS Track 2) resolves{{ appIntent.X }}placeholders against parameters stored in DataStore, populated by an in-appUI (the PoC's stand-in for a future Glance configuration activity).
The architectural claim: one JS resolver bundle, two platforms, with strictly more expressiveness
than opcode tuples. PoC scope is narrow — prove engine integration and the reactivity loop. Does
not (yet) replace the native opcode-tuple resolver from #130 or add Glance configuration
activities (analog of #147).
Related
poc/widget-reactivity-track-1,poc/widget-reactivity-track-2,poc/widget-reactivity-track-3,poc/widget-reactivity-track-4