leptos-rs/leptos · error
you are using leptos_meta without a </head> tag
Error message
you are using leptos_meta without a </head> tag
What it means
During server-side rendering, leptos_meta rewrites the first HTML chunk to insert title/meta tags. It expects the string "</head>" to be present in that chunk and panics with this message if it cannot be found. The library throws it because the streaming-injection strategy requires a literal closing head tag to anchor insertion.
Source
Thrown at meta/src/lib.rs:251
.map(|n| "<title>".len() + n.len() + "</title>".len())
.unwrap_or(0);
// collect all registered meta tags
let meta_buf = self.elements.try_iter().collect::<String>();
// get HTML strings for `<html>` and `<body>`
let html_attrs = self.html.try_iter().collect::<String>();
let body_attrs = self.body.try_iter().collect::<String>();
let mut modified_chunk = if title_len == 0 && meta_buf.is_empty() {
first_chunk
} else {
let mut buf = String::with_capacity(
first_chunk.len() + title_len + meta_buf.len(),
);
let head_loc = first_chunk
.find("</head>")
.expect("you are using leptos_meta without a </head> tag");
let marker_loc = first_chunk
.find("<!--HEAD-->")
.map(|pos| pos + "<!--HEAD-->".len())
.unwrap_or_else(|| {
first_chunk.find("</head>").unwrap_or(head_loc)
});
let (before_marker, after_marker) =
first_chunk.split_at_mut(marker_loc);
buf.push_str(before_marker);
buf.push_str(&meta_buf);
if let Some(title) = title {
buf.push_str("<title>");
buf.push_str(&title);
buf.push_str("</title>");
}
buf.push_str(after_marker);
buf
};View on GitHub (pinned to 32d20f6c9d)
Solutions
- Ensure the app template's first streamed chunk contains a literal "</head>" (lowercase, with the closing slash).
- Optionally include the "<!--HEAD-->" marker comment in <head> so leptos_meta can anchor injection there.
- Disable HTML minification/rewriting of the head in your SSR pipeline, or configure it to preserve "</head>" verbatim.
- Verify the head markup isn't split across stream chunks before leptos_meta processes it.
Example fix
<!-- before: transformed shell --> <head><meta charset="utf-8"/> <!-- after --> <head><meta charset="utf-8"><!--HEAD--></head>
Defensive patterns
Strategy: validation
Validate before calling
if !first_chunk.contains("</head>") {
return Err(anyhow!("SSR shell must contain a literal </head> for leptos_meta"));
} Type guard
fn shell_has_head_close(html: &str) -> bool {
html.contains("</head>")
} Prevention
- Keep a lowercase literal "</head>" in the first streamed chunk.
- Add the <!--HEAD--> marker comment inside <head>.
- Disable minifiers/rewriters that alter the head markup in SSR output.
When it happens
Trigger: Calling from_app/inject_meta_context with an HTML template whose first chunk lacks the literal "</head>" — e.g. minified/transformed templates, templates using uppercase <HEAD>, or head content streamed in a later chunk.
Common situations: Custom React-like templates or framework wrappers that emit <head/> self-closed or omit the closing tag; build tools (minifiers, HTML rewriters) reformatting "</head>"; injecting leptos_meta into apps whose shell doesn't use a standard head.
Related errors
- body to exist
- no shared context
- children should not be removed until we render here
- missing <head> element
- failed to read hash file
AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01).
Data as JSON: /api/errors/aef6e3091235f56e.
Report an issue: GitHub.