Skip to content

Compose Remote Layout

Server-Driven UI Component for Compose Multiplatform

Maven Central Version Publish to Maven Central

Overview

Compose Remote Layout empowers you to dynamically update your UI without app store submissions. Built for Compose Multiplatform, this library transforms JSON into native UI components, giving you the flexibility to modify interfaces on the fly.

By enabling server-driven UI within the Compose ecosystem, you can:

  • Update your app's look and feel instantly
  • A/B test different layouts with different user segments
  • Fix UI issues without emergency releases
  • Deliver platform-specific experiences from a central source

Why Use Compose Remote Layout?

While solutions like React Native and Flutter offer dynamic UI updates, they require learning new frameworks or languages. Compose Remote Layout brings these capabilities to Compose developers:

  • No New Languages to Learn - Stay within the Compose ecosystem
  • Native Performance - Uses standard Compose components under the hood
  • Multiplatform Support - Works with Compose Multiplatform for Android, iOS, Desktop, and Web
  • Granular Control - Update specific screens or components, not the entire app
  • Lightweight - Minimal impact on app size and performance

Key Features

Component System

The library provides built-in support for all essential Compose components:

// JSON definition
val json = """
{
  "column": {
    "children": [
      { "text": { "content": "Hello World!" } },
      { "button": { "content": "Click Me", "clickId": "my_button" } }
    ]
  }
}
"""

// Simple rendering
DynamicLayout(component = createLayoutComponent(json))
  • Core Components - Column, Row, Box, Text, Button, Card, Spacer, and Grid
  • Modifiers - Complete modifier support matching native Compose capabilities
  • Nested Components - Create complex layouts with unlimited nesting

Dynamic Updates

Load layouts from various sources to update your UI without redeployment:

// From Firebase Remote Config
remoteConfig.fetchAndActivate().addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val layoutJson = remoteConfig.getString("home_screen")
        val component = createLayoutComponent(layoutJson)
        DynamicLayout(component = component)
    }
}
  • Multiple Sources - API responses, Firebase Remote Config, local files, databases
  • Fallback Support - Graceful degradation when remote sources are unavailable
  • Caching - Store layouts for offline use and faster loading

Value Binding

Connect your layouts to dynamic data with the BindsValue system:

// Create and populate bindings
val bindsValue = remember { BindsValue() }
bindsValue.setValue("username", user.displayName)
bindsValue.setValue("itemCount", cart.items.size.toString())

// Reference in JSON with {key} syntax
val json = """
{
  "text": {
    "content": "Welcome, {username}! You have {itemCount} items in your cart."
  }
}
"""
  • Real-time Updates - Values update automatically when underlying data changes
  • Type Support - Bind to text content, colors, font sizes, and other properties
  • Composable Integration - Works seamlessly with Compose state management

Action Binding

Create interactive UIs with click event handling:

DynamicLayout(
    component = component,
    onClickHandler = { clickId ->
        when (clickId) {
            "login_button" -> viewModel.login()
            "signup_button" -> navController.navigate("signup")
            "settings" -> openSettings()
        }
    }
)
  • Event Handling - Connect clicks to your application logic
  • Parameterized Actions - Pass data through structured click IDs
  • Architecture Integration - Works with ViewModel, MVI, and other patterns

Custom Components

Extend the library with your own components:

// Register a custom component
CustomNodes.register("profile_card") { param ->
    val name = param.data["name"] ?: "Unknown"
    val avatarUrl = param.data["avatar_url"]

    Card(modifier = param.modifier) {
        // Your custom implementation
    }
}

// Use in JSON
val json = """
{
  "profile_card": {
    "name": "John Doe",
    "avatar_url": "https://example.com/avatar.jpg"
  }
}
"""
  • Custom UI Elements - Create reusable components that fit your app's needs
  • Data Passing - Send arbitrary data to custom components
  • Composition Support - Custom components can contain other components

Cross-Platform

Develop once, deploy everywhere:

  • Android - Native support through Jetpack Compose
  • iOS - Support through Compose Multiplatform or standalone Swift package
  • Desktop - Works with Compose for Desktop
  • Web - Compatible with Compose for Web

Use Cases

A/B Testing

Deploy multiple layout variations to different user segments:

val layoutKey = when {
    user.isInTestGroup("new-home-ui") -> "home_new"
    user.isInBetaProgram() -> "home_beta"
    else -> "home_standard"
}

val layoutJson = remoteConfig.getString(layoutKey)

Dynamic Content

Update UI for seasonal changes, promotions, or feature announcements without app updates:

val layoutJson = when {
    isHolidaySeason() -> remoteConfig.getString("home_holiday")
    isPromoActive() -> remoteConfig.getString("home_promo")
    hasNewFeature() -> remoteConfig.getString("home_new_feature")
    else -> remoteConfig.getString("home_default")
}

Platform Customization

Deliver optimized experiences for different devices:

val layoutKey = when {
    isTablet() -> "product_detail_tablet"
    isLandscape() -> "product_detail_landscape"
    else -> "product_detail_phone"
}

Rapid Iterations

Quickly fix UI issues or test new designs without app store submissions:

// Fetch the latest layout version
val layoutJson = api.fetchLayout("checkout_screen", buildConfig.VERSION_CODE)

// Apply with fallback for errors
try {
    val component = createLayoutComponent(layoutJson)
    DynamicLayout(component = component)
} catch (e: Exception) {
    // Fall back to bundled layout
    val bundledJson = loadJsonFromAssets("checkout_fallback.json")
    val fallbackComponent = createLayoutComponent(bundledJson)
    DynamicLayout(component = fallbackComponent)
}

Getting Started

Get started with Compose Remote Layout:

Setup

Learn more about the library's core features:

Sample Projects

Explore complete examples in the repository:

- * Firebase Integration ** - Complete implementation with Firebase Remote Config - * Custom Components ** - Examples of extending the library - * *Form Builder ** - Dynamic form creation and validation

JSON Builder Tool

The repository includes a web-based JSON builder tool for creating and testing layouts:

JSON Builder Tool

To use the JSON builder:

./gradlew :jsonBuilderWeb:jsBrowserRun

Current Status

⚠️ Early Development Stage

Compose Remote Layout is currently in alpha stage. While functional for many use cases, please note:

  • API may change between versions
  • Test thoroughly before production use
  • Not all Compose features are supported yet
  • Performance optimizations are ongoing

We recommend:

  • Using this library for experimental projects
  • Contributing feedback and bug reports
  • Waiting for stable releases before critical production use

Get Involved