Supported on: iOS SDK
1.0.70+ · Android SDK 1.0.42+ · React Native SDK 1.0.7+ (New Architecture only)Supported Formats
Lottie Animations
Lottie animations are rendered natively on both platforms. On iOS the SDK useslottie-ios (added as a separate SPM dependency in your iOS host project), and on Android Lottie Compose is bundled directly into the AppDNA SDK.
- Remote URL — provide a URL to a
.jsonLottie file - Inline JSON — embed the animation JSON directly in the config
- Controls — autoplay, loop, speed, play-on-scroll, play-on-tap
- Color overrides — remap colors in the animation without re-exporting
Rive Animations
Rive animations are rendered using the native Rive runtime on each platform. iOS requires addingrive-ios as a separate SPM dependency in your iOS host project. The Android Rive runtime is bundled with the AppDNA Android SDK.
- Remote URL — provide a URL to a
.rivfile - Artboard selection — choose which artboard to display
- State machine — drive interactive animations with state machine inputs
- Trigger on completion — fire a state machine trigger when the step or flow completes
Video
Inline video playback uses native players on each platform — AVPlayer on iOS, ExoPlayer on Android. React Native sees a fully rendered native view, so playback performance is identical to a hand-written native implementation.- Remote URL — provide a URL to an MP4 or HLS video
- Thumbnail — optional poster image shown before playback
- Controls — autoplay, loop, muted, show/hide controls
- Sizing — configurable height and corner radius
Images
Remote images are fetched and cached by the native SDKs:- Remote URL — standard JPEG, PNG, and WebP supported on both platforms
- GIF — animated GIFs are supported in in-app message hero areas and push notification rich content
- SVG — supported on both platforms with native rasterization
- Corner radius, sizing, content mode — configured in the Console
Asset URL Conventions
Each rich-media block in the AppDNA Console uses a typed URL field:
Any of these fields can also accept inline data where supported (e.g. Lottie supports inline JSON). The SDK auto-detects the format from the URL extension or the inline payload.
Embedding Rich Media in Paywalls
Rich media inside paywalls is configured per section. The console exposes section types likepaywall_header, paywall_features, and paywall_cta, each of which can host an image, Lottie animation, Rive animation, or video. The React Native SDK simply presents the paywall and the native renderer handles the rest:
- Hero Lottie — attach a Lottie URL to the
paywall_headersection to play a looping product animation while the user reads features. - Feature illustrations — inside
paywall_features, each feature row can carry an inline icon (Lucide / SF Symbols / Material / emoji) or a remote image. - Video proof — a short looping product demo can be embedded into a section with
paywall_videostyle. - CTA pulse — the
paywall_ctasection supports CTA animations (pulse, glow, bounce) configured entirely from the Console.
Embedding Rich Media in Onboarding
Onboarding steps are composed of an ordered list of content blocks. Each block has a type and optional styling. Supported block types include:
Blocks are added in the Console — the React Native SDK simply presents the flow:
Embedding Rich Media in In-App Messages
In-app messages support a hero area at the top of the modal, banner, or fullscreen layout. The hero can host:- An image (with optional GIF playback)
- A Lottie animation
- A short looping video
AppDNAInAppMessageDelegate:
Animation Playback Control
All animation playback is configured per section / per content block in the AppDNA Console. The native renderers honor the data fields embedded in the config:
These fields are wired automatically by the renderer — you do not need to forward them through any TypeScript bridge.
Local Asset Embedding
For shipping assets inside the app bundle (instead of downloading them at runtime), use platform-native asset locations:- iOS — drop the
.json/.riv/ video file into your iOS host project’s asset catalog or main bundle. - Android — drop the asset into
app/src/main/assets/(orres/raw/for video). - Console — reference the bundled asset with an
asset://URL (e.g.asset://intro.json).
asset:// URLs against the host app bundle before falling back to network. Assets bundled via the Metro packager (require('./assets/...')) are not visible to the native renderers, so put bundled rich media in the iOS / Android native locations described above.
Fallback Behavior
The native renderers degrade gracefully when an asset cannot be loaded:- Lottie / Rive — if the file is missing, malformed, or the network request fails, the block is skipped (no empty space, no crash). The next block in the layout takes its slot.
- Video — if the video URL is unreachable, the optional
thumbnail_urlis shown instead. If no thumbnail is configured, the block is skipped. - Image — if the image URL is unreachable, the block is skipped. There is no broken-image placeholder.
- Inline JSON Lottie — if the inline JSON is malformed, the block is skipped and a debug log is emitted.
Performance Notes
- Lazy loading — rich-media blocks are loaded on demand. A Lottie / Rive / video asset is only fetched when its block enters the visible viewport.
- Asset caching — remote assets are cached by the native HTTP stack (URLCache on iOS, OkHttp disk cache on Android). Subsequent presentations reuse the cached file.
- Memory — the native renderers tear down decoded animations when the host view is dismissed, so animations do not accumulate memory across flows.
- Cold start — avoid placing large video files on the very first onboarding step. Use a Lottie or static image for the welcome step and put video on later steps so playback starts after configuration is fully loaded.
- Inline JSON — inline Lottie payloads add to the config bundle size. Prefer remote URLs for animations larger than ~50 KB to keep config refreshes fast.
Icons
The SDK supports structured icon references across all modules. Icons resolve to platform-native equivalents:
Icons are used in onboarding content blocks, paywall feature lists, in-app message CTA buttons, and survey question options.
Visual Effects
Visual effects (haptics, particle effects, blur backdrops) are rendered by the native SDKs and configured in the Console. The React Native SDK does not need any wiring — effects fire automatically based on the triggers attached to the section or step.Haptic Feedback
Haptic feedback is triggered automatically based on Console configuration. Supported haptic types:
Haptic triggers can be configured for:
- Step advances (onboarding)
- Button taps
- Plan selection (paywalls)
- Option selection (surveys, forms)
- Toggle changes
- Form submission
- Error and success states
Particle Effects
Particle effects overlay the current screen. Supported effect types:
Each effect has a trigger (
on_appear, on_step_complete, on_purchase, on_flow_complete), duration, intensity (light, medium, heavy), and optional custom colors.
Blur / Glassmorphism
Blur backdrops use the native platform’s high-performance blur primitive (iOSUIVisualEffectView, Android RenderEffect) for performant glassmorphism effects:
- Radius — blur intensity
- Tint — overlay color tint
- Saturation — color saturation adjustment
Styling
All visual elements support fine-grained styling through the Console:- Text styles — font family, size, weight, color, alignment, line height, letter spacing, opacity
- Backgrounds — solid color, linear/radial gradient, or remote image with overlay
- Borders — width, color, style, and per-corner radius
- Shadows — offset, blur, spread, and color
- Spacing — per-side padding and margin
- Animations — entry animations (slide, fade, scale), section stagger, CTA pulse/glow/bounce, dismiss animations
The native SDKs use platform-native fonts only. Custom web fonts are not supported. Specify system font families in the Console and the SDK will render them using each platform’s default typeface.
Dynamic Content Templates
Rich media content supports template interpolation using{{variable}} syntax. Available namespaces:
Use the pipe syntax for fallback values:
{{user.name | there}} renders as "there" if the user name is not set.

