Ctx Callback Factory
Ctx<Msg> is the callback factory passed to Component::view(). It creates event handlers that send messages to the component's update loop.
Creating a Ctx
Ctx is created by the runtime — you never create one manually. It's passed to view():
fn view(&self, props: &Props, ctx: &Ctx<Msg>, env: &Env) -> El { ... }
Mouse Callbacks
on_click(msg)
Sends a pre-built message on click:
el::button()
.text("Increment")
.on_click_cb(ctx.on_click(Msg::Increment))
callback(|ev| Msg)
Maps an event to a message:
el::div()
.on_click_cb(ctx.callback(|ev: &MouseEvent| {
Msg::ClickedAt(ev.pos.0, ev.pos.1)
}))
Other mouse targets
el::button().on_mouse_down_cb(ctx.on_click(Msg::Pressed))
el::button().on_mouse_up_cb(ctx.on_click(Msg::Released))
el::div().on_mouse_enter_cb(ctx.on_click(Msg::Hovered))
el::div().on_mouse_leave_cb(ctx.on_click(Msg::Unhovered))
All _cb variants accept Arc<dyn Fn(&MouseEvent)> returned by Ctx methods.
Event Callbacks
event_callback(|ev| Option<Msg>)
Maps any HtmlEvent to an optional message. Return None to ignore the event:
el::input().on_event_cb(ctx.event_callback(|ev: &HtmlEvent| {
if let HtmlEventType::Input { data } = &ev.event_type {
Some(Msg::InputChanged(data.clone()))
} else {
None
}
}))
on_event (raw closure)
el::div().on_event(|ev: &HtmlEvent| {
// Handle any event type
})
MsgSender
pub struct MsgSender<M> {
queue: Arc<Mutex<Vec<M>>>,
wake: Arc<dyn Fn() + Send + Sync>,
}
impl<M> MsgSender<M> {
pub fn send(&self, msg: M);
}
Obtain via Ctx::sender():
let sender = ctx.sender();
// Store sender, use later from another thread
std::thread::spawn(move || {
let result = fetch_data();
sender.send(Msg::DataLoaded(result));
});
Scoped CSS Classes
let scoped_class = ctx.scoped("highlight");
// If scope() == "my-component" → "my-component-highlight"
// If scope() == "" → "highlight"
el::div().class(&ctx.scoped("card"))
scoped() Helper
When Component::scope() returns a prefix, Ctx::scoped("class") prepends it:
fn scope() -> &'static str { "my-component" }
// In view():
el::div().class(&ctx.scoped("btn")) // → class="my-component-btn"
This ensures CSS isolation without manual class name management.
Child Component Embedding
child::<C>(props)
Embed a child component at the current position:
el::div().children([
el::h2().text("User List"),
ctx.child::<UserList>(UserListProps { filter: "active".into() }),
])
Returns an El containing a marker node — the runtime replaces it with the child's rendered output.
keyed_child::<C>(key, props)
Keyed variant for stable identity across re-renders:
items.iter().enumerate().map(|(i, item)| {
ctx.keyed_child::<ItemRow>(
&format!("item-{}", item.id), // stable key
ItemRowProps { item: item.clone() },
)
})
Keyed children survive reordering and insertion without unmounting. The key is (String, TypeId).
Background Thread → Message
fn view(&self, _props: &Props, ctx: &Ctx<Msg>, _env: &()) -> El {
let sender = ctx.sender();
el::button()
.text("Load Data")
.on_click(move |_| {
let sender = sender.clone();
std::thread::spawn(move || {
let data = fetch_from_api();
sender.send(Msg::DataLoaded(data));
});
})
}
MsgSender::send() enqueues the message and calls wake() to trigger a re-render. The runtime drains all pending messages during process().