leptos-rs/leptos · error

missing <head> element

Error message

missing <head> element

What it means

MetaContext::default builds a cursor positioned at the head-marker comment inside <head> so metadata can be injected during hydration. It unwraps document().head(), panicking with "missing <head> element" if the current document has no <head>. The library assumes a well-formed HTML document exists when leptos_meta runs on the client.

Source

Thrown at meta/src/lib.rs:122

    pub(crate) cursor: Arc<LazyLock<SendWrapper<Cursor>>>,
}

impl MetaContext {
    /// Creates an empty [`MetaContext`].
    pub fn new() -> Self {
        Default::default()
    }
}

pub(crate) const HEAD_MARKER_COMMENT: &str = "HEAD";
/// Return value of [`Node::node_type`] for a comment.
/// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#node.comment_node
const COMMENT_NODE: u16 = 8;

impl Default for MetaContext {
    fn default() -> Self {
        let build_cursor: fn() -> SendWrapper<Cursor> = || {
            let head = document().head().expect("missing <head> element");
            let mut cursor = None;
            let mut child = head.first_child();
            while let Some(this_child) = child {
                if this_child.node_type() == COMMENT_NODE
                    && this_child.text_content().as_deref()
                        == Some(HEAD_MARKER_COMMENT)
                {
                    cursor = Some(this_child);
                    break;
                }
                child = this_child.next_sibling();
            }
            SendWrapper::new(Cursor::new(
                cursor
                    .expect(
                        "no leptos_meta HEAD marker comment found. Did you \
                         include the <MetaTags/> component in the <head> of \
                         your server-rendered app?",

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Ensure the document being hydrated contains a <head> element (valid HTML shell).
  2. Create MetaContext only in browser environments where a full document exists.
  3. In tests, build the DOM with a <head> containing the <!--HEAD--> marker comment if you exercise meta hydration.
  4. Do not strip <head> from the served markup before hydration.

Example fix

<!-- before: shell missing head -->
<body>...</body>

<!-- after -->
<head><!--HEAD--><title>app</title></head>
<body>...</body>
Defensive patterns

Strategy: validation

Validate before calling

if document().head().is_none() {
    console_error!("document has no <head>; leptos_meta unavailable");
    return;
}

Type guard

fn has_head() -> bool {
    document().head().is_some()
}

Prevention

When it happens

Trigger: Constructing MetaContext::default() (directly or via leptos_meta's provider/hydration path) when document().head() returns None — e.g. a document with no <head> section, or running in an environment with an incomplete DOM.

Common situations: Custom SSR shells that omit <head>, streaming responses where the head chunk was already consumed/removed, or test DOMs built without head elements.

Related errors


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