yewstack/yew · error

global document not set

Error message

global document not set

What it means

This panic is `Document::new().expect("global document not set")` in the web-sys guide. Despite the message, `web_sys::Document::new()` does not fetch the global document — it invokes the JS `Document` constructor. In engines or embeddings without that constructor it throws, and wasm-bindgen surfaces the thrown value as the `Err` this expect panics on; where the constructor exists it returns a brand-new empty document, which then guarantees the follow-up `get_element_by_id` expect (error 67) fails too. The global document must be obtained via `gloo::utils::document()` or `web_sys::window()?.document()?`.

Source

Thrown at website/versioned_docs/version-0.22/concepts/basic-web-technologies/web-sys.mdx:182

```rust ,no_run
use wasm_bindgen::{prelude::Closure, JsCast};
use web_sys::{console, Document, HtmlElement, MouseEvent};

let mousemove = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|e| {
    let rect = e
        .target()
        .expect("mouse event doesn't have a target")
        .dyn_into::<HtmlElement>()
        .expect("event target should be of type HtmlElement")
        .get_bounding_client_rect();
    let x = (e.client_x() as f64) - rect.left();
    let y = (e.client_y() as f64) - rect.top();
    console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
}));

Document::new()
    .expect("global document not set")
    .get_element_by_id("mousemoveme")
    .expect("element with id `mousemoveme` not present")
    .unchecked_into::<HtmlElement>()
    .set_onmousemove(mousemove.as_ref().dyn_ref());

// we now need to save the `mousemove` Closure so that when
// this event fires the closure is still in memory.
```

This version is much more verbose, but you will probably notice part of that is because of failure
types reminding us that some of these function calls have invariants that must be held, or otherwise will
cause a panic in Rust. Another part of the verbosity is the calls to `JsCast` to cast into
different types so that you can call its specific methods.

### Yew example

In Yew you will mostly be creating [`Callback`](concepts/function-components/callbacks.mdx)s to use in the
[`html!`](concepts/html/introduction.mdx) macro so the example is going to use this approach instead of completely copying

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Replace `Document::new()` with `gloo::utils::document()` — the guide's own portal example uses exactly this accessor
  2. Or use the explicit form `web_sys::window().expect("no window").document().expect("no document")`
  3. Keep DOM-touching code out of worker/SSR code paths (gate with `#[cfg(target_arch = "wasm32")]` and a browser feature check)

Example fix

// before
Document::new()
    .expect("global document not set")
    .get_element_by_id("mousemoveme")

// after
let document = web_sys::window()
    .and_then(|w| w.document())
    .expect("no global window/document");
document.get_element_by_id("mousemoveme")
// or simply: gloo::utils::document().get_element_by_id("mousemoveme")
Defensive patterns

Strategy: validation

Validate before calling

fn global_document() -> Option<web_sys::Document> {
    web_sys::window().and_then(|w| w.document())
}

match global_document() {
    Some(doc) => { /* DOM work */ },
    None => gloo::console::warn("no global document (worker/SSR?)"),
}

Prevention

When it happens

Trigger: Running the example in an environment without a full browser DOM (web worker, SSR, minimal test harness) or an engine lacking the `Document` constructor; any code path that copies the guide's `Document::new()` line verbatim instead of using the global-document accessor.

Common situations: Copy-pasting the web-sys.mdx mousemove example into a Yew app that already depends on gloo; Trunk SSR builds; unit tests executed in Node without a DOM polyfill.

Related errors


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