Live Activities and widgets.
No Swift, no Kotlin.

Voltra turns JSX into SwiftUI and Jetpack Compose Glance. You and your agent ship Live Activities, Dynamic Island layouts and widgets on iOS and Android without writing Swift or Kotlin.

Voltrazsh
$ npm install @use-voltra/ios @use-voltra/ios-client
# for you: JSX primitives and the runtime APIs
$ npx skills add callstackincubator/voltra
# for your agent: the Voltra skill

Works with Live Activities, Dynamic Island, iOS widgets, Android widgets, Ongoing notifications, SwiftUI, Jetpack Compose Glance, Expo, React Native CLI, APNs, FCM.

[01] What you get

Every native surface, from the components you know.

[02] Push updates

Same JSX on your server. Pushed to the lock screen.

Render a Live Activity on your server from the same components and send it over APNs. Widgets update over FCM on Android. Your app doesn’t need to be running.

Read the push guide
Voltrapush.tsx
import { Voltra, renderLiveActivityToString } from '@use-voltra/ios-server'

const payload = renderLiveActivityToString({
  lockScreen: (
    <Voltra.VStack style={{ padding: 16 }}>
      <Voltra.Text>Driver arrived</Voltra.Text>
      <Voltra.Text>Ready for pickup</Voltra.Text>
    </Voltra.VStack>
  ),
})

[03] How it works

Install, configure, prebuild. Then it’s all React.

  1. 01

    Install

    Add the platform package for JSX and the client package for runtime APIs.

    $ npm install @use-voltra/ios @use-voltra/ios-client
  2. 02

    Configure

    Add the Voltra plugin to app.json. It wires the widget and Live Activity extension targets.

    app.json → plugins
  3. 03

    Prebuild

    Generate the native projects, then start your app as usual.

    $ npx expo prebuild --clean
VoltraOrderTracker.tsx
import { Voltra } from '@use-voltra/ios'
import { useLiveActivity } from '@use-voltra/ios-client'

export function OrderTracker({ orderId }: { orderId: string }) {
  const ui = (
    <Voltra.VStack style={{ padding: 16 }}>
      <Voltra.Text>Order #{orderId}</Voltra.Text>
      <Voltra.Text>Driver en route · ETA 12 min</Voltra.Text>
    </Voltra.VStack>
  )

  useLiveActivity({ lockScreen: ui }, {
    activityName: `order-${orderId}`,
    autoStart: true,
  })

  return null
}
@use-voltra/ios-clientLive Activities, Dynamic Island and home screen widgets, rendered as SwiftUI.Set up iOS

[04] Native, without native code

The config plugin owns native. You own the components.

Generated targets

The config plugin, or npx voltra apply, creates and updates the extension targets for you.

Payload budgets

Both the JS and native layers check payload sizes against each platform’s limits.

Separate server packages

@use-voltra/ios-server and @use-voltra/android-server render payloads in your backend, apart from the app.

Typed end to end

Components, hooks and the plugin config ship TypeScript definitions.

[05] FAQ

Frequently asked questions

Do I need to write Swift or Kotlin?
No. You write Voltra JSX and call Voltra’s JS APIs. The config plugin generates the native extension targets for you.
Does it work in Expo Go?
No. Voltra adds native extension targets, so it needs an Expo Dev Client or a bare React Native app.
Can I use it without Expo?
Yes. In React Native CLI projects, npx voltra apply updates the native project instead of the Expo config plugin.
Which iOS version do I need?
iOS 16.4 or later. Some features need newer versions: push-to-start needs iOS 17.2, broadcast push needs iOS 18.
Can my server update a Live Activity?
Yes. @use-voltra/ios-server renders the same JSX to a payload you send over APNs, and @use-voltra/android-server does the same for widgets over FCM.
I’m on Voltra 1. How do I upgrade?
Voltra 2 splits the library into platform packages. The migration guide lists every import that moved.
Get started