# How Does Event Handling Work Across Different Dioxus Renderers?

> Discover how Dioxus unifies event handling across Web, Desktop, Native, and LiveView renderers. Learn how native UI events become typed objects for shared component code.

- Repository: [Dioxus Labs/dioxus](https://github.com/DioxusLabs/dioxus)
- Tags: internals
- Published: 2026-07-23

---

**Dioxus unifies event handling across all platforms by funneling native UI events through a central `Runtime` as synthetic, typed objects, allowing Web, Desktop, Native, and LiveView renderers to share identical component code while only differing in their native event translation layer.**

Dioxus is a Rust library for building cross-platform user interfaces that target web browsers, desktop applications, mobile devices, and server-side live views. Understanding how event handling works across different Dioxus renderers is essential for writing platform-agnostic components that respond consistently to user interactions regardless of the target environment.

## The Core Event Architecture

At the heart of Dioxus lies a **centralized `Runtime`** that manages event dispatch for every supported platform. When a user interacts with your application, the specific renderer (Web, Desktop, Native, or LiveView) captures the native event and translates it into a **synthetic event**—a typed object wrapped in `Event<dyn Any>`.

The runtime exposes the primary dispatch method in [`packages/core/src/runtime.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/core/src/runtime.rs):

```rust
Runtime::handle_event(name, event, element_id)

```

This method receives the event name (e.g., `"click"`), the synthetic event payload, and the target element's ID. According to the source code in [`packages/core/src/events.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/core/src/events.rs), the runtime stores event listeners within the Virtual DOM and executes them in **bubbling order** when the `event_bubbles` flag permits propagation.

## Renderer-Specific Event Bridges

Each renderer implements a thin translation layer that converts platform-native events into Dioxus's synthetic format before delegating to the shared runtime.

### Web Renderer

The Web renderer bridges browser DOM events to the Dioxus runtime. In [`packages/web/src/mutations.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/web/src/mutations.rs), the `create_event_listener` method registers native `addEventListener` callbacks on DOM elements:

```rust
fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
    self.websys_dom
        .new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
}

```

When the browser fires a native event, the Web renderer constructs a `Synthetic<ClickEvent>` (or appropriate type from `packages/web/src/events/*.rs`) and invokes `runtime.handle_event`. The renderer also manages the **mounted event queue** via `flush_queued_mounted_events`, ensuring `onmounted` lifecycle hooks execute exactly once after DOM node creation.

### Desktop Renderer

For desktop applications, Dioxus embeds a WebView that communicates with the Rust backend through a specialized protocol. The Desktop renderer, implemented in [`packages/desktop/src/webview.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/desktop/src/webview.rs), intercepts events from the embedded WebView and forwards them to the core runtime. This allows desktop applications to use the same event handling patterns as web applications while operating within a native window context.

### Native Renderer

The Native renderer targets mobile and embedded platforms without relying on WebView technology. In [`packages/native-dom/src/dioxus_document.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/native-dom/src/dioxus_document.rs), the renderer attaches native OS callbacks directly to platform-specific UI elements. These callbacks translate touch, gesture, and input events into the synthetic Dioxus format before calling the shared runtime methods.

### LiveView Renderer

LiveView enables server-rendered applications where user interactions travel over WebSocket connections. The LiveView renderer in [`packages/liveview/src/pool.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/liveview/src/pool.rs) receives serialized events from the client, reconstructs them as synthetic events on the server, and dispatches them through the same `Runtime::handle_event` path used by client-side renderers.

## Event Bubbling and Propagation Control

Dioxus respects standard DOM event propagation semantics through the `event_bubbles` function defined in `dioxus_core_types`. When `Runtime::handle_event` processes an event, it checks this function to determine whether the event should propagate upward through the component tree.

The runtime executes listeners in **bubbling order**—from the target element up through its ancestors—respecting any propagation flags that listeners may set to stop further bubbling. This behavior remains consistent across all renderers because the bubbling logic lives entirely within the platform-agnostic core runtime rather than in renderer-specific code.

## The Mounted Event Lifecycle

Beyond user interactions, Dioxus handles component lifecycle events through a special **"mounted"** event system. When a component first renders, the runtime queues a mounted event for each element. After the renderer creates the corresponding native UI nodes, it calls `flush_queued_mounted_events` to fire these queued events exactly once.

This mechanism ensures that `onmounted` callbacks execute only after the element exists in the native view hierarchy, whether that hierarchy consists of browser DOM nodes, Desktop WebView elements, or Native platform views.

## Practical Implementation Examples

The following examples demonstrate how Dioxus event handling works in practice across different renderers.

**Standard component usage** (works identically on every platform):

```rust
#[component]
fn Counter() -> Element {
    let mut count = use_signal(|| 0);
    rsx! {
        button {
            onclick: move |_| count += 1,
            "Clicked {count} times"
        }
    }
}

```

The `onclick` attribute registers a listener that the runtime invokes upon receiving a `"click"` event for that button's element ID.

**Manual event dispatch** (useful for testing or programmatic control):

```rust
use dioxus::prelude::*;
use dioxus_core::Event;

fn trigger_custom_event(dom: &VirtualDom) {
    // Create a synthetic event payload (any type implementing Any)
    let payload = Rc::new("my payload".to_string()) as Rc<dyn Any>;
    let event = Event::new(payload);
    // Dispatch to element with id 42
    dom.runtime().handle_event("my_event", event, ElementId(42));
}

```

Because all renderers ultimately call `runtime.handle_event`, this approach works regardless of whether the application runs in a browser, desktop window, or native mobile shell.

## Summary

- Dioxus treats all UI events as **synthetic, typed objects** processed through a central `Runtime` shared by every renderer.
- Each renderer (Web, Desktop, Native, LiveView) translates native platform events into Dioxus's format before calling `Runtime::handle_event` in [`packages/core/src/runtime.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/core/src/runtime.rs).
- Event bubbling behavior is controlled by the `event_bubbles` function and enforced consistently across platforms during listener execution.
- **Mounted events** are queued during rendering and flushed once native nodes exist, enabling reliable `onmounted` lifecycle hooks.
- Component code remains identical across renderers because event dispatch logic lives in the platform-agnostic core rather than renderer-specific implementations.

## Frequently Asked Questions

### Do I need to write different event handling code for Web versus Desktop renderers?

No. Dioxus abstracts platform differences through synthetic events. Your component code using attributes like `onclick` or `oninput` works identically across Web, Desktop, Native, and LiveView renderers because each renderer translates native events into the same format before reaching your application logic.

### How does Dioxus handle events that don't bubble, like `focus` or `blur`?

The runtime checks the `event_bubbles` function (defined in `dioxus_core_types`) before propagating events. For non-bubbling events, the runtime executes only the listeners attached to the target element without traversing up the component tree, matching standard browser behavior while maintaining cross-platform consistency.

### Can I manually trigger events programmatically in Dioxus?

Yes. You can construct an `Event` object with any payload implementing `Any` and dispatch it through `Runtime::handle_event` by accessing the virtual DOM's runtime. This works universally because all renderers rely on the same core runtime method for event processing.

### Where does the event bubbling logic live in the codebase?

Event bubbling logic resides in [`packages/core/src/runtime.rs`](https://github.com/DioxusLabs/dioxus/blob/main/packages/core/src/runtime.rs) within the `Runtime::handle_event` implementation. The runtime looks up registered listeners for the target element and executes them in bubbling order, checking propagation flags after each invocation to determine whether to continue up the tree.