yewstack/yew · error

can't create element for vtag

Error message

can't create element for vtag

What it means

On first mount of a VTag without an xmlns attribute (and not under svg/math ancestry), Yew calls document().create_element(tag) and panics with this message if the browser rejects the call. createElement throws InvalidCharacterError for any tag string that is not a valid element name: empty strings, embedded whitespace, characters like '/', '<', '>', or names starting with a digit. In practice this panic means the tag string handed to the virtual DOM is malformed.

Source

Thrown at packages/yew/src/dom_bundle/btag/mod.rs:301

                    document()
                        .create_element_ns(namespace, tag)
                        .expect("can't create namespaced element for vtag")
                } else {
                    thread_local! {
                        static CACHED_ELEMENTS: RefCell<HashMap<String, Element>> = RefCell::new(HashMap::with_capacity(32));
                    }

                    CACHED_ELEMENTS.with(|cache| {
                        let mut cache = cache.borrow_mut();
                        let cached = cache.get(tag).map(|el| {
                            el.clone_node()
                                .expect("couldn't clone cached element")
                                .unchecked_into::<Element>()
                        });
                        cached.unwrap_or_else(|| {
                            let to_be_cached = document()
                                .create_element(tag)
                                .expect("can't create element for vtag");
                            cache.insert(
                                tag.to_string(),
                                to_be_cached
                                    .clone_node()
                                    .expect("couldn't clone node to be cached")
                                    .unchecked_into(),
                            );
                            to_be_cached
                        })
                    })
                }
            }
        }
    }
}

impl BTag {
    /// Get the key of the underlying tag

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Log or inspect the exact tag string at the call site that builds the VTag - it violates the HTML element-name rules
  2. Validate and normalize before constructing the node: trim, lowercase, then match ^[a-z][a-z0-9-]*$ (require a hyphen for custom elements)
  3. Fall back to a safe default tag such as span, or render nothing, when the name is invalid
  4. For SVG/MathML children, place them under an <svg> or <math> parent so Yew uses the namespaced create_element_ns path instead of the plain path

Example fix

// before
let tag = user_input.clone(); // e.g. "My Widget"
html! { <@{tag}>{ "hi" }</@> }

// after
fn normalize_tag(input: &str) -> Option<String> {
    let t = input.trim().to_ascii_lowercase();
    (!t.is_empty()
        && t.starts_with(|c: char| c.is_ascii_lowercase())
        && t.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-'))
    .then_some(t)
}
match normalize_tag(&user_input) {
    Some(tag) => html! { <@{tag}>{ "hi" }</@> },
    None => html! { <span class="invalid-tag">{ "unsupported tag" }</span> },
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Run before building the VTag / dynamic tag:
fn normalize_tag(input: &str) -> Option<String> {
    let t = input.trim().to_ascii_lowercase();
    (!t.is_empty()
        && t.starts_with(|c: char| c.is_ascii_lowercase())
        && t.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-'))
    .then_some(t)
}

Type guard

fn is_valid_tag_name(tag: &str) -> bool {
    let t = tag.trim();
    !t.is_empty()
        && t.starts_with(|c: char| c.is_ascii_lowercase())
        && t.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
}

Prevention

When it happens

Trigger: Building a tag name from an unvalidated string - VTag::new(tag), dynamic tags such as <@{tag}>...</@> in the html! macro, or tag names read from config/user input - where the string is empty, contains whitespace or '/', or starts with a non-letter.

Common situations: Tag names computed at runtime from CMS data, user input, or config without validation; format!/trim chains that can produce an empty string; typos like 'my widget' (space) or 'div/' (trailing slash); forgetting that custom element names must contain a hyphen.

Related errors


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