How Airbnb’s Trio Props Enable Type‑Safe, Dynamic Communication Between Compose Pages
This article explains Airbnb’s Android Trio framework, focusing on Props—a Kotlin data‑class mechanism that lets parent pages safely pass mutable data and callbacks to child Compose pages, illustrated with inbox and Todo screen‑flow examples and complete code snippets.
What is Trio and why Props matter
Trio is Airbnb’s Android page‑architecture framework built on Jetpack Compose and the Mavericks state‑management library. In the third part of the series, the focus is on Props, a Kotlin data‑class that allows parent pages to transmit mutable, type‑safe data and callbacks to child pages.
Inbox example with two child Trios
A simple inbox UI consists of a list Trio on the left and a detail Trio on the right. The parent page holds a ParentState data class that stores the message list, the selected message, and the two child Trios.
data class ParentState(
val inboxMessages: List<Message>,
val selectedMessage: Message?,
val messageListScreen: Trio<ListProps>,
val messageDetailScreen: Trio<DetailsProps>
) : MavericksStateThe rendering logic uses Compose to show the two child pages side‑by‑side in landscape or a single page in portrait, switching based on LocalConfiguration.current.orientation.
@Composable
override fun TrioRenderScope.Content(state: ParentState) {
if (LocalConfiguration.current.orientation == ORIENTATION_LANDSCAPE) {
Row(Modifier.fillMaxSize()) {
ShowTrio(state.messageListScreen, modifier = Modifier.weight(1f))
ShowTrio(state.messageDetailScreen)
}
} else {
if (state.selectedMessage == null) {
ShowTrio(state.messageListScreen)
} else {
BackHandler { viewModel.clearMessageSelection() }
ShowTrio(state.messageDetailScreen)
}
}
}Defining Props
Props are Kotlin data classes stored in the parent’s state and passed to child Trios. Unlike static Arguments, Props can change over time, carry lambdas for callbacks, and are type‑checked at compile time.
data class ListProps(
val selectedMessage: Message?,
val inboxMessages: List<Message>,
val onMessageSelected: (Message) -> Unit
)
data class DetailProps(
val selectedMessage: Message?
)Passing Props from the parent ViewModel
The parent ViewModel creates Props inside init using launchChildInitializer. The lambda receives the current state and returns a new Props instance for the target child.
class ParentViewModel : TrioViewModel {
init {
launchChildInitializer({ messageListScreen }) { state ->
ListProps(state.selectedMessage, state.inboxMessages, ::showMessageDetails)
}
launchChildInitializer({ messageDetailScreen }) { state ->
DetailProps(state.selectedMessage)
}
}
fun showMessageDetails(message: Message?) { /* … */ }
}When the parent state changes, the lambda is re‑executed, delivering fresh Props to the children.
Using Props inside child ViewModels
Child ViewModels override updateStateFromPropsChange to merge Props into their own State, ensuring the UI reflects the latest data.
class ListViewModel : TrioViewModel<ListProps, ListState> {
override fun updateStateFromPropsChange(newProps: ListProps, thisState: ListState): ListState {
return thisState.copy(
inboxMessages = newProps.inboxMessages,
selectedMessage = newProps.selectedMessage
)
}
fun onMessageSelected(message: Message) {
props.onMessageSelected(message)
}
}Standardized page‑flow Props (Todo example)
Trio also provides a “page‑flow” pattern where a parent flow Trio manages a stack of child screens and shares a common Props object.
data class TodoFlowState(
@PersistState
override val childScreenTransactions: List<ScreenTransaction<TodoFlowProps>> = listOf(
ScreenTransaction(Router.TodoListScreen.createFullPaneTrio(NoArgs))
),
val todoListQuery: TodoList?
) : ScreenFlowState<TodoFlowState, TodoFlowProps>
data class TodoFlowProps(
val navController: NavController<TodoFlowProps>,
val todoListQuery: TodoList?,
val reloadList: () -> Unit
)The NavController interface lets child screens push or pop other screens within the flow.
interface NavController<PropsT> {
fun push(router: TrioRouter<*, in PropsT>)
fun pop()
}The flow ViewModel creates Props for each child and updates them whenever the flow state changes.
class TodoScreenFlowViewModel(initializer: Initializer<NavPopProps, TodoFlowState>) :
ScreenFlowViewModel<NavPopProps, TodoFlowProps, TodoFlowState>(initializer) {
override fun createFlowProps(state: TodoFlowState, props: NavPopProps): TodoFlowProps {
return TodoFlowProps(
navController = this,
state.todoListQuery,
::reloadList
)
}
}Child ViewModels receive the shared Props and can navigate using the injected navController.
class TodoListViewModel(initializer: Initializer<TodoFlowProps, TodoListState>) :
TrioViewModel<TodoFlowProps, TodoListState>(initializer) {
override fun updateStateFromPropsChange(newProps: TodoFlowProps, thisState: TodoTaskState): TodoTaskState {
return thisState.copy(todoListQuery = newProps.todoListQuery)
}
fun navigateToTodoTask(task: TodoTask) {
props.navController.push(Router.TodoTaskScreen, TodoTaskArgs(task.id))
}
}Adoption and impact at Airbnb
Since its first production rollout in mid‑2022, more than 230 Trio pages have been shipped at Airbnb, handling large traffic volumes. Developer surveys report that Props simplify callback sharing, reduce boilerplate, and make Compose‑only development faster and more enjoyable.
Developer tooling
Airbnb built an internal Android Studio plugin that generates Trio scaffolding—files, routes, mocks, and tests—and assists developers in selecting Arguments and Props, streamlining refactoring of Props or Args across the codebase.
Future directions
When Compose adds native shared‑element transition APIs, Trio plans to incorporate those transitions and possibly redesign its navigation API to support richer animations.
Summary
Trio’s Props mechanism provides a clear, type‑safe way to pass mutable data and callbacks between Compose pages, enabling scalable page‑flow architectures and improving developer productivity across hundreds of production screens at Airbnb.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Airbnb Technology Team
Official account of the Airbnb Technology Team, sharing Airbnb's tech innovations and real-world implementations, building a world where home is everywhere through technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
