Developing Live Activities
Voltra provides APIs that make building and testing Live Activities easier during development.
For bundled, on-device definitions that receive generic props rather than a rendered UI payload, see Dynamic Live Activities.
Supported variants
Section titled “Supported variants”Live Activities in iOS can appear in different contexts, and Voltra supports defining UI variants for each of these contexts. For detailed information about Live Activity design guidelines, see the Apple Human Interface Guidelines.
Lock Screen
Section titled “Lock Screen”The lockScreen variant defines how your Live Activity appears on the lock screen. It can be either a ReactNode directly, or an object with content and optional styling:
const variants = { lockScreen: ( <Voltra.VStack> <Voltra.Text>Your content here</Voltra.Text> </Voltra.VStack> ),}To customize the system Lock Screen chrome, pass an object with content and activityBackgroundTint:
const variants = { lockScreen: { activityBackgroundTint: '#101828', content: ( <Voltra.VStack> <Voltra.Text>Your content here</Voltra.Text> </Voltra.VStack> ), },}activityBackgroundTint is applied via SwiftUI’s activityBackgroundTint(...) modifier on iOS. Voltra currently accepts the same color formats handled by the native iOS parser:
- Hex colors such as
#RGB,#RGBA,#RRGGBB, and#RRGGBBAA rgb(...)andrgba(...)hsl(...)andhsla(...)- Named colors:
red,orange,yellow,green,mint,teal,cyan,blue,indigo,purple,pink,brown,white,gray,black,clear,transparent,primary,secondary
Use clear or transparent to make the Live Activity background transparent.
If the string cannot be parsed as one of those formats, iOS ignores the tint.
The tint changes the Lock Screen and banner background. It does not change the background of the Live Activity in CarPlay; see Background color in CarPlay.
Dynamic Island
Section titled “Dynamic Island”The island variant defines how your Live Activity appears in the Dynamic Island (available on iPhone 14 Pro and later). The Dynamic Island has three display states:
- Minimal: A compact pill-shaped view that appears when the activity is in the background
- Compact: A slightly larger view with leading and trailing regions
- Expanded: A full-width view with center, leading, trailing, and bottom regions
const variants = { island: { keylineTint: '#10B981', // Optional tint color for the Dynamic Island keyline minimal: <Voltra.Symbol name="checkmark.circle.fill" tintColor="#10B981" />, compact: { leading: <Voltra.Text>Order</Voltra.Text>, trailing: <Voltra.Text>Confirmed</Voltra.Text>, }, expanded: { center: <Voltra.Text style={{ fontSize: 16, fontWeight: '600' }}>Order Confirmed</Voltra.Text>, leading: <Voltra.Symbol name="checkmark.circle.fill" />, trailing: <Voltra.Text>ETA: 15 min</Voltra.Text>, bottom: <Voltra.Text style={{ fontSize: 12 }}>Your order is being prepared</Voltra.Text>, }, },}keylineTint uses the same iOS color parser and accepted formats as activityBackgroundTint.
Supplemental Activity Families (iOS 18+, watchOS 11+)
Section titled “Supplemental Activity Families (iOS 18+, watchOS 11+)”The supplementalActivityFamilies variant defines how your Live Activity appears on Apple Watch Smart Stack and CarPlay displays. This variant is optional and works seamlessly with your existing lock screen and Dynamic Island variants.
const variants = { lockScreen: ( <Voltra.VStack> {/* iPhone lock screen content */} </Voltra.VStack> ), island: { /* Dynamic Island variants for iPhone */ }, supplementalActivityFamilies: { small: ( <Voltra.HStack style={{ padding: 12, gap: 8 }}> <Voltra.Text style={{ fontSize: 18, fontWeight: '700' }}>12 min</Voltra.Text> <Voltra.Text style={{ fontSize: 14, color: '#9CA3AF' }}>ETA</Voltra.Text> </Voltra.HStack> ), },}If supplementalActivityFamilies.small is not provided, Voltra will automatically construct it from your Dynamic Island compact variant by combining the leading and trailing content in an HStack.
Background color in CarPlay
Section titled “Background color in CarPlay”In CarPlay, activityBackgroundTint has no effect. iOS draws the small layout on its own dark grey tile in both light and dark appearance, and containerBackground and preferredColorScheme don’t change it either. This is iOS behavior: Apple documents activityBackgroundTint as the background color of the Lock Screen presentation only.
To use a different background in CarPlay, give the root of your small layout flex: 1 and a backgroundColor. An opaque fill covers the whole tile:
supplementalActivityFamilies: { small: ( <Voltra.VStack style={{ flex: 1, backgroundColor: '#101828' }}> <Voltra.Text style={{ fontSize: 18, fontWeight: '700', color: '#FFFFFF' }}>12 min</Voltra.Text> </Voltra.VStack> ),},Keep in mind:
- Padding on the root without
flex: 1leaves the grey tile visible around the content. - A translucent color blends with the grey tile and doesn’t replace it.
- The same
smalllayout is also used on Apple Watch Smart Stack, so check how a filled background looks there. - This was verified in Xcode 27’s CarPlay Simulator on an iPhone running iOS 27, not on a car head unit.
Limitations
Section titled “Limitations”Animations and Live Updates
Section titled “Animations and Live Updates”There are specific constraints on how content can animate or update:
- Continuous Animations: Custom continuous animations, such as rotating icons or elements moving along a path, are not supported.
- Smooth Updates: Per-second “live” updates are only supported by specific components designed for this purpose:
Timer: For countdowns and stopwatches.LinearProgressView: When used withtimerInterval.
- Styling Trade-offs: To enable smooth, system-driven animations (like a progress bar filling up in real-time), certain components may ignore custom styling properties (e.g., custom heights or thumb components) and fallback to standard system appearances.
All other components only update their visual state when a new activity state is pushed from your application.
useLiveActivity
Section titled “useLiveActivity”For React development, Voltra provides the useLiveActivity hook for integration with the component lifecycle and automatic updates during development.
import { Voltra } from '@use-voltra/ios'import { useLiveActivity } from '@use-voltra/ios-client'
function OrderLiveActivity({ orderId, status }) { const variants = { lockScreen: ( <Voltra.VStack style={{ padding: 16, borderRadius: 18, backgroundColor: '#101828' }}> <Voltra.Text style={{ color: '#F8FAFC', fontSize: 18, fontWeight: '600' }}> {status === 'confirmed' ? 'Order Confirmed' : 'Order Ready'} </Voltra.Text> <Voltra.Text style={{ color: '#94A3B8', fontSize: 12, marginTop: 8 }}> {status === 'confirmed' ? 'Your order is being prepared' : 'Your order is ready for pickup'} </Voltra.Text> {status === 'ready' && ( <Voltra.Button onPress="pickup-order" style={{ marginTop: 12 }}> I'm Here </Voltra.Button> )} </Voltra.VStack> ), }
const { start, update, end, isActive } = useLiveActivity(variants, { activityName: `order-${orderId}`, autoStart: true, // Automatically start when component mounts autoUpdate: true, // Automatically update when variants change deepLinkUrl: `myapp://order/${orderId}`, })
// Manual control if needed const handleCancelOrder = async () => { await end() }
return ( <View> <Text>Live Activity: {isActive ? 'Active' : 'Inactive'}</Text> <Button onPress={handleCancelOrder} title="Cancel Order" /> </View> )}VoltraView Component
Section titled “VoltraView Component”For testing and development, Voltra provides a VoltraView component that renders Voltra JSX components directly in your React Native app, so you can test layouts and interactions before deploying to a real Live Activity.
import { Voltra } from '@use-voltra/ios'import { VoltraView } from '@use-voltra/ios-client'
function MyComponent() { const handleInteraction = (event: VoltraInteractionEvent) => { console.log('User interacted with:', event.identifier) console.log('Payload:', event.payload) }
return ( <VoltraView id="my-test-view" onInteraction={handleInteraction} style={{ height: 200, borderRadius: 12, overflow: 'hidden' }} > <Voltra.VStack style={{ padding: 16, backgroundColor: '#101828' }}> <Voltra.Text style={{ color: '#F8FAFC', fontSize: 18, fontWeight: '600' }}>Test Live Activity</Voltra.Text> <Voltra.Text style={{ color: '#94A3B8', fontSize: 12, marginTop: 8 }}>This is how it will look</Voltra.Text> <Voltra.Button id="test-button" style={{ marginTop: 12 }}> <Voltra.Text>Test Button</Voltra.Text> </Voltra.Button> </Voltra.VStack> </VoltraView> )}Props:
id: Unique identifier for the view (used for event filtering)children: Voltra JSX components to renderstyle: React Native style for the containeronInteraction: Callback for user interactions with buttons/toggles
Hook Options:
activityName: Name of the Live ActivityautoStart: Automatically start when component mountsautoUpdate: Automatically update when variants changedeepLinkUrl: URL to open when Live Activity is tapped
Hook Returns:
start(): Start the Live Activityupdate(): Update the Live Activityend(): Stop the Live ActivityisActive: Boolean indicating if the Live Activity is currently active