yewstack/yew · error

no body node found

Error message

no body node found

What it means

Renderer::new()/with_props defaults to mounting into gloo::utils::document().body(); this expect fires when document.body is null at the moment the Renderer is constructed. That means the entry code ran before the HTML body was parsed, or executes in an environment that simply has no body element. It is a bootstrap-order problem, not a rendering failure.

Source

Thrown at packages/yew/src/renderer.rs:80

        Self::default()
    }

    /// Creates a [Renderer] that renders into a custom root with default properties.
    pub fn with_root(root: Element) -> Self {
        Self::with_root_and_props(root, Default::default())
    }
}

impl<COMP> Renderer<COMP>
where
    COMP: BaseComponent + 'static,
{
    /// Creates a [Renderer] that renders into the document body with custom properties.
    pub fn with_props(props: COMP::Properties) -> Self {
        Self::with_root_and_props(
            gloo::utils::document()
                .body()
                .expect("no body node found")
                .into(),
            props,
        )
    }

    /// Creates a [Renderer] that renders into a custom root with custom properties.
    pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
        Self { root, props }
    }

    /// Renders the application.
    pub fn render(self) -> AppHandle<COMP> {
        set_default_panic_hook();
        AppHandle::<COMP>::mount_with_props(self.root, Rc::new(self.props))
    }
}

#[cfg(feature = "hydration")]

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Add defer to the script tag, or move it to the end of <body>
  2. Defer construction and render until DOMContentLoaded fires
  3. Mount into an explicit existing element with Renderer::with_root(document().get_element_by_id("app").unwrap())
  4. In tests, make sure the harness document actually contains a <body>

Example fix

// before: runs immediately; if the script is in <head>, body does not exist yet
#[wasm_bindgen(start)]
fn start() {
    yew::Renderer::<App>::new().render();
}

// after: mount into a known element, with the script deferred or at the end of <body>
#[wasm_bindgen(start)]
fn start() {
    let root = gloo::utils::document()
        .get_element_by_id("app")
        .expect("missing #app mount point");
    yew::Renderer::<App>::with_root(root).render();
}
Defensive patterns

Strategy: validation

Validate before calling

// Guard before constructing the default Renderer:
match gloo::utils::document().body() {
    Some(_) => yew::Renderer::<App>::new().render(),
    None => {
        // body not parsed yet - defer or fail with a clear message
    }
}

Prevention

When it happens

Trigger: Constructing Renderer inside #[wasm_bindgen(start)] or module-level code while the script loads in <head> without defer (body not yet parsed); running the CSR entry in a DOM host without a body element (bare test harnesses, workers).

Common situations: Bundler or framework templates that move the script into <head>; removing defer/type=module attributes; hydration entries that run during initial parse; headless tests using minimal document fixtures.

Related errors


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