leptos-rs/leptos · error

there to be a <body> element

Error message

there to be a <body> element

What it means

Leptos' <body> meta component (tachys RenderHtml impl) builds by fetching document().body() via web_sys. In a non-DOM or pre-DOM environment this is None, and expect('there to be a <body> element') panics. The library assumes the code runs in a browser after the document body exists.

Source

Thrown at meta/src/body.rs:70

struct BodyView<At> {
    attributes: At,
}

struct BodyViewState<At>
where
    At: Attribute,
{
    attributes: At::State,
}

impl<At> Render for BodyView<At>
where
    At: Attribute,
{
    type State = BodyViewState<At>;

    fn build(self) -> Self::State {
        let el = document().body().expect("there to be a <body> element");
        let attributes = self.attributes.build(&el);

        BodyViewState { attributes }
    }

    fn rebuild(self, state: &mut Self::State) {
        self.attributes.rebuild(&mut state.attributes);
    }
}

impl<At> AddAnyAttr for BodyView<At>
where
    At: Attribute,
{
    type Output<SomeNewAttr: Attribute> =
        BodyView<<At as NextAttribute>::Output<SomeNewAttr>>;

    fn add_any_attr<NewAttr: Attribute>(

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Ensure the code only runs in a browser context after the DOM is ready (e.g. inside on_mount or after DOMContentLoaded).
  2. Guard with a check: if document().body().is_none() return early instead of building the Body component.
  3. For tests, run in a wasm browser test harness (wasm-bindgen-test) rather than native cargo test.

Example fix

// before
let body = document().body().expect("there to be a <body> element");
// after
let Some(body) = document().body() else { return; };
Defensive patterns

Strategy: validation

Validate before calling

use wasm_bindgen::JsCast;
fn has_body() -> bool {
    web_sys::window().map(|w| w.document()).flatten()
        .map(|d| d.body().is_some())
        .unwrap_or(false)
}

Type guard

fn get_body() -> Option<web_sys::HtmlElement> {
    web_sys::window()?.document()?.body()
}

Try / catch

// leptos panics are not catchable; check before mounting
if has_body() {
    mount_to_body(App);
} else {
    leptos::logging::error!("document body not available");
}

Prevention

When it happens

Trigger: Calling .build() on leptos::prelude::Body (the <body> meta wrapper) when document().body() returns None — e.g. running in SSR/non-wasm stub, or executing before the HTML <body> tag has been parsed.

Common situations: Unit tests running wasm-dependent code without a DOM; scripts injected into <head> that run before <body> exists; executing hydration/mount logic inside a worker or server target.

Related errors


AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01). Data as JSON: /api/errors/64952a1f3f058d0c. Report an issue: GitHub.