superwall-kmp
0.3.1indexedServer-driven native paywall rendering backend JSON into Material 3 UI, with A/B testing, subscription management, variable templates, expression evaluator, DI, billing integrations and WebView fallback.
Server-driven native paywall rendering backend JSON into Material 3 UI, with A/B testing, subscription management, variable templates, expression evaluator, DI, billing integrations and WebView fallback.
Kotlin Multiplatform SDK for Superwall — remote paywall configuration, A/B testing, and subscription management from a single codebase.
The backend defines the entire paywall UI as a JSON component tree — the SDK renders it as native Material 3 composables across all platforms. No WebView, no HTML, no CSS.
Backend JSON Native UI
───────────── ─────────
{ "type": "stack", Column {
"dimension": "vertical", → Text("Unlock Premium")
"components": [ PackageSelector(...)
{ "type": "text", ... }, Button("Start Free Trial")
{ "type": "package", ... }, }
{ "type": "purchase_button" }
]
}
{{ product.price }} → $49.99
{{ product.period }} → year
{{ product.trial_days }} → 14
{{ product.price_per_month }} → USD 4.16
{{ product.name }} → Yearly
Box {
MyApp()
SuperwallNativePaywall()
}
When componentsConfig is present in the backend response, the SDK renders natively. When absent, it falls back to WebView — fully backwards compatible.
dependencies {
implementation("io.github.androidpoet:superwall:0.2.0")
implementation("io.github.androidpoet:superwall-compose:0.2.0")
}
Superwall.configure(
apiKey = "pk_...",
platformDependencies = superwallAndroidDependencies(
context = applicationContext,
activityProvider = { currentActivity },
),
)
Superwall.configure(
apiKey = "pk_...",
platformDependencies = superwallIOSDependencies,
)
SuperwallCompanion.shared.configure(
apiKey: "pk_...",
platformDependencies: IOSModuleKt.superwallIOSDependencies
)
Superwall.instance.register("premium_feature") {
println("Unlocked")
}
Superwall.instance.identify("user_123")
Superwall.instance.setUserAttributes(mapOf("plan" to "pro", "age" to 25))
Superwall.instance.setSubscriptionStatus(
SubscriptionStatus.Active(entitlements = setOf(Entitlement("premium")))
)
SuperwallGate(placement = "premium_feature") {
Text("This is premium content!")
}
SuperwallPaywall(
placement = "upgrade_prompt",
onFeatureUnlocked = { /* navigate */ },
onDismiss = { /* handle dismiss */ },
)
val controller = object : PurchaseController {
override suspend fun purchase(product: StoreProduct): PurchaseResult {
return PurchaseResult.Purchased
}
override suspend : RestorationResult {
RestorationResult.Restored
}
}
Superwall.configure(
apiKey = ,
options = SuperwallOptions(purchaseController = controller),
platformDependencies = superwallAndroidDependencies(context, activityProvider),
)
Superwall.instance.events.collect { eventInfo ->
println("${eventInfo.event} at ${eventInfo.timestamp}")
}
Superwall.instance.delegate = object : SuperwallDelegate {
override fun handleSuperwallEvent { }
{ }
{ }
}
# All targets
./gradlew build
# Android only
./gradlew :superwall:compileReleaseKotlinAndroid
# iOS only
./gradlew :superwall:compileKotlinIosArm64
# Desktop (JVM)
./gradlew :superwall:compileKotlinDesktop
Contributions are welcome! If you've found a bug, have an idea for an improvement, or want to contribute new features, please open an issue or submit a pull request.
Support it by joining stargazers for this repository. :star:
Also, follow me on GitHub for my next creations! 🤩
Copyright 2026 androidpoet (Ranbir Singh)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance the License.
You may obtain a copy of the License at
http:
Unless applicable law agreed to writing, software
distributed under the License distributed an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
See the License the specific language governing permissions
limitations under the License.
commonMainSuperwallPaywall, SuperwallGate, and SuperwallNativePaywall composables| Component | Renders As | Purpose |
|---|
stack | Column / Row / Box | Layout container (vertical, horizontal, z-layer) |
text | Material 3 Text | With {{ product.price }} variable resolution |
image | Platform image loader | Remote URL or local asset |
button | Clickable Box | Fires actions (close, open-url, custom) |
purchase_button | Clickable Box | Triggers purchase flow |
package | Selectable card | Product option with animated selection style |
spacer | Spacer | Flexible or fixed spacing |
divider | HorizontalDivider | Thin separator line |
close_button | Icon button | Dismisses the paywall |
badge | Styled label | "BEST VALUE", "SAVE 58%", etc. |
icon | Icon or symbol | Named icon or URL |
| Platform | Target | Billing | Rendering |
|---|
| Android | androidTarget | Google Play Billing 7.1 | Native Compose + WebView fallback |
| iOS | iosArm64, iosX64, iosSimulatorArm64 | StoreKit 1 | Native Compose + WKWebView fallback |
| macOS | macosArm64, macosX64 | — | Native Compose |
| Desktop | jvm("desktop") | — | Native Compose |
superwall-kmp/
├── superwall/ ← Core SDK (KMP)
│ ├── commonMain/ ← Shared business logic
│ │ ├── Superwall.kt ← Entry point
│ │ ├── models/ ← Domain models
│ │ │ └── components/ ← Server-driven UI component model
│ │ │ ├── PaywallComponent ← 11 sealed component types
│ │ │ ├── Styling ← Colors, padding, borders, shadows
│ │ │ ├── Action ← Purchase, close, restore, navigate
│ │ │ └── VariableResolver ← {{ product.price }} template engine
│ │ ├── config/ ← Remote config + caching
│ │ ├── identity/ ← User identity + attributes
│ │ ├── analytics/ ← Event tracking + batching
│ │ ├── placement/ ← Trigger evaluation + expression engine
│ │ ├── network/ ← Ktor API client
│ │ └── di/ ← dependency factory
│ ├── androidMain/ ← Android implementations
│ │ ├── billing/ ← Google Play Billing
│ │ └── webview/ ← WebView + JS bridge + Activity
│ └── iosMain/ ← iOS implementations
│ ├── storekit/ ← StoreKit 1
│ └── webview/ ← WKWebView + message handlers
├── superwall-compose/ ← Compose Multiplatform UI
│ ├── PaywallComposable.kt ← SuperwallPaywall, SuperwallGate
│ └── renderer/ ← Server-driven native renderer
│ ├── ComponentRenderer ← Recursive component → Compose mapper
│ ├── NativePaywall ← Top-level rendering composable
│ ├── PaywallRenderState ← Variable resolution + product state
│ └── SuperwallNativePaywall ← Auto-presenting drop-in composable
├── desktop-sample/ ← Desktop JVM sample (3 paywall designs)
└── app/ ← Android sample
| Layer | Library |
|---|
| Networking | Ktor 3.0 |
| Serialization | kotlinx.serialization 1.7 |
| Async | kotlinx.coroutines 1.9 |
| Date/Time | kotlinx-datetime 0.6 |
| Build | Gradle 8.9, Kotlin 2.1.0, AGP 8.5.2 |
| Publishing | Maven Central via vanniktech |
Surfaced from shared tags and platforms — no rankings paid for.