Messaging
Type-safe message passing between components, bubbling up the tree.
Messages let a deeply nested child component notify an ancestor without threading callbacks through every intermediate component. A message bubbles up from child to parent until it reaches the root or is stopped.
Defining a message
A message is a data class that extends MessageBase:
data class ItemSelected(
override val sender: Component<*>,
val itemId: String,
) : MessageBase<Component<*>>(sender) The sender field tracks which component sent the message. Add any payload fields you need — here
it's itemId.
Sending a message
Call sendMessage from any component:
class ItemCard(ctx: Ctx<Props>) : Component<ItemCard.Props>(ctx) {
data class Props(val item: Item)
override fun VDom.render() {
ui.card {
onClick {
sendMessage(ItemSelected(sender = this@ItemCard, itemId = props.item.id))
}
noui.content {
noui.header { +props.item.name }
}
}
}
} The message dispatches on the sender's parent, then continues up the tree. The sender itself does not receive its own message.
Listening for messages
Register a listener in your component's init block with onMessage:
class ItemBrowser(ctx: NoProps) : PureComponent(ctx) {
private var selectedId: String? by value(null)
init {
onMessage<ItemSelected> { msg ->
selectedId = msg.itemId
}
}
override fun VDom.render() {
ui.cards {
items.forEach { item ->
ItemCard(item)
}
}
selectedId?.let { id ->
p { +"Selected: $id" }
}
}
} The type parameter on onMessage<ItemSelected> filters by message type — only ItemSelected
messages trigger this handler.
Stopping propagation
By default, messages continue bubbling after a handler processes them. To stop a message from reaching further
ancestors, call stop():
onMessage<ItemSelected> { msg ->
selectedId = msg.itemId
msg.stop() // no ancestor will see this message
} When to use messaging
| Pattern | When to use |
|---|---|
| Callback prop | Direct parent-child. The parent passes onSelect: (String) -> Unit as a prop. |
| Message | Deeply nested child needs to notify a distant ancestor. Intermediaries don't need to know. |
| Stream | Cross-cutting state that multiple unrelated components observe. See State Management. |