Skip to main content
Supported on: iOS SDK 1.0.70+ · Android SDK 1.0.42+ · React Native SDK 1.0.7+ (New Architecture only)

Overview

Server-driven screens let growth teams design and deploy native screens from the AppDNA Console without an app release. Screens are composed from the same content-block and section library used by paywalls, onboarding, in-app messages, and surveys, then delivered to the SDK via the remote config bundle. On React Native, screens are rendered fully natively — SwiftUI on iOS and Jetpack Compose on Android — so the experience matches a hand-built native UI. Use screens to ship feature announcements, upgrade prompts, referral surfaces, guided tutorials, and other composable native surfaces from the Console.

Show a Screen

show(...) resolves a boolean: whether the screen was presented. It does not resolve the outcome, and it cannot: a screen can be dismissed minutes after it appears. What the user did arrives on onScreenDismissed on the delegate you register with setDelegate(...) — see Screen Delegate below.

Show a Multi-Screen Flow

showFlow(...) likewise resolves whether the flow was presented. The result — completion, the screens viewed, the responses — arrives on the delegate’s onFlowCompleted.

Dismiss

<AppDNAScreenSlot> — inline screens

show(...) and showFlow(...) present a screen over your app. A slot embeds one inside your own layout: you place <AppDNAScreenSlot name="..."> in your React tree, the Console assigns a screen to that slot name, and the native renderer draws it in place. It is the only React component the package exports.
  • An empty slot renders nothing — no visual impact and no reserved space once measured.
  • Content updates on the next config refresh.
  • Audience targeting applies per slot.
  • The slot raises no delegate callbacks: onScreenPresented / onScreenDismissed / onFlowCompleted belong to show(...) / showFlow(...). A slot is content, not a presentation.
iOS + use_frameworks!: one required step. If your Podfile uses use_frameworks! (any app with Firebase in its pod graph ends up needing it, and the AppDNA core SDK depends on Firebase), React Native’s generated component registry is compiled out — nothing registers the slot’s native view. There is no error and no warning: <AppDNAScreenSlot> simply renders React’s Unimplemented component: <AppdnaScreenSlotView> placeholder and your slot never appears.Add this override to AppDelegate.mmRCTAppDelegate already exposes the hook:
Only the inline slot needs this. Every method, event, and delegate works without it. Apps that link pods as static libraries need nothing.
Inject screens automatically between app navigations:
When interception is enabled, the SDK consults the registered AppDNAScreenDelegate on every navigation event so you can route or suppress as needed. Pass an array of route names to limit interception to an allowlist — exact names and trailing * wildcards.

Debug Preview

Render a screen from raw JSON for design or QA preview without publishing to the Console: preview takes the screen config as a JSON string, not an object:
It resolves a boolean — whether the preview was presented. Use during development only.

Module Access

Access the screen module through the AppDNA.screens namespace:

Audience-Based Presentation

In addition to imperative show(...) calls, screens can be presented automatically by Console-driven audience targeting:
  1. Design the screen in the AppDNA Console.
  2. Attach a trigger (event-based, schedule-based, or app-state-based) and an audience.
  3. Publish the screen.
When a user enters the audience, the SDK fetches the latest config bundle and presents the screen at the configured trigger point. The required wiring is minimal:
  • Call AppDNA.configure(...) at app startup.
  • Identify the user with AppDNA.identify(...) so targeting rules can resolve.
  • Track relevant events with AppDNA.track(...) so event triggers can fire.
Lifecycle callbacks fire on the registered delegate either way.

Screen Delegate

AppDNAScreenDelegate exposes the four screen lifecycle methods. The delegate is implemented as an object literal or class and registered with the module:
Pass null to clear a delegate. Registering a new delegate replaces the previous one, and AppDNA.shutdown() detaches everything. Register once at app startup rather than from a component that mounts and unmounts.

Delegate Method Reference

Result and action payloads are delivered as Record<string, unknown> rather than typed classes. This keeps the bridge forward-compatible with new fields the native SDKs add (for example, additions like last_action or duration_ms) without requiring a React Native package bump.

Sections and Content Blocks

Screens are composed of an ordered list of sections. Each section pulls from the unified registry shared across all SDK modules, so the same content blocks you use in paywalls, onboarding, in-app messages, and surveys are available inside screens: Sections are assembled in the Console and stream to the SDK through the config bundle. The React Native SDK does not need to know which sections a screen contains — the native renderer reads the configuration and lays the screen out.

Presentation Modes

Screens support several presentation modes configured per screen in the Console. The native renderer honors each mode on the appropriate platform: Unrecognized values fall through to fullscreen on Android. The React Native SDK does not need to pick a mode — the Console controls the presentation per screen.

Auto-Tracked Events

The native SDKs emit standard analytics events for every screen interaction. These events flow through the same event pipeline as other AppDNA events: You do not need to forward or duplicate these events from TypeScript — the native SDK already emits them.

Configuration in Console

Each screen is configured in the AppDNA Console with:
  • Screen ID — the canonical identifier used by audience triggers and analytics events.
  • Layout — scroll, stack, or grid container that wraps the section list.
  • Sections — an ordered list pulled from the unified section registry.
  • Presentationfullscreen, modal, bottom_sheet, or push.
  • Trigger — event-based (event_name matching), schedule-based (cron-style), or app-state-based.
  • Audience — targeting rules across user traits, device info, remote config, computed values, and experiment exposures.
  • Style — per-screen theme, typography overrides, brand colors, and animation defaults.
Publish the screen and it is delivered to all SDKs on the next config refresh.

Full Example

Wire it into your app startup:
Screens are delivered via the same Firestore-backed config bundle as other SDK modules. Ensure your app has network access on first launch to fetch the latest screen configurations. After the first successful fetch, the native SDKs cache the bundle and continue rendering screens offline.