yewstack/yew · error

Event should have a target when dispatched

Error message

Event should have a target when dispatched

What it means

This panic is the `.expect()` on `Event::target()` in Yew 0.22's events guide, identical to the 0.21 listing. `web_sys::Event::target()` returns `Option<EventTarget>` because the DOM permits targetless events (constructed-but-undispatched events, `window`-dispatched events). Yew forwards the raw browser event to the `Callback<Event>`, so a None target makes the expect panic and aborts the WASM app. Genuine user-driven `change` events on an `<input>` always carry a target, so this is almost exclusively a test/synthetic-dispatch problem.

Source

Thrown at website/versioned_docs/version-0.22/concepts/html/events.mdx:155

        Callback::from(move |e: Event| {
            // When events are created the target is undefined, it's only
            // when dispatched does the target get added.
            let target: Option<EventTarget> = e.target();
            // Events can bubble so this listener might catch events from child
            // elements which are not of type HtmlInputElement
            //highlight-next-line
            let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());

            if let Some(input) = input {
                input_value_handle.set(input.value());
            }
        })
    };

    let on_dangerous_change = Callback::from(move |e: Event| {
        let target: EventTarget = e
            .target()
            .expect("Event should have a target when dispatched");
        // You must KNOW target is a HtmlInputElement, otherwise
        // the call to value would be Undefined Behaviour (UB).
        // Here we are sure that this is input element so we can convert it to the appropriate type without checking
        //highlight-next-line
        input_value_handle.set(target.unchecked_into::<HtmlInputElement>().value());
    });

    html! {
        <>
            <label for="cautious-input">
                { "My cautious input:" }
                <input onchange={on_cautious_change}
                    id="cautious-input"
                    type="text"
                    value={input_value.clone()}
                />
            </label>
            <label for="dangerous-input">

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Handle the Option: `let Some(target) = e.target() else { return; };`
  2. Use `e.target_dyn_into::<HtmlInputElement>()` — presence check plus type check in one call, as shown later in the same guide
  3. Dispatch synthetic events from the real input element in tests so the target is populated

Example fix

// before
let target: EventTarget = e
    .target()
    .expect("Event should have a target when dispatched");
input_value_handle.set(target.unchecked_into::<HtmlInputElement>().value());

// after
if let Some(input) = e.target_dyn_into::<HtmlInputElement>() {
    input_value_handle.set(input.value());
}
Defensive patterns

Strategy: type-guard

Validate before calling

let on_dangerous_change = Callback::from(move |e: Event| {
    let Some(target) = e.target() else {
        return;
    };
    let _ = target;
});

Type guard

use web_sys::{Event, HtmlInputElement};

fn input_target(e: &Event) -> Option<HtmlInputElement> {
    e.target_dyn_into::<HtmlInputElement>()
}

Prevention

When it happens

Trigger: A `wasm-bindgen-test` invoking the `on_dangerous_change` callback with a hand-built `Event` (target None); dispatching `change` through `window.dispatchEvent`; event retargeting wrappers in test utilities.

Common situations: Copy-pasting the events.mdx `unchecked_into` recipe (line ~155) from the 0.22 docs into apps or tests; upgrading from Yew versions where handlers received typed event wrappers and now receive plain `Event`.

Related errors


AI-assisted analysis of yewstack/yew@0e4a05472f (2026-08-22). Data as JSON: /api/errors/7f9eb609097cb766. Report an issue: GitHub.