yewstack/yew · error · syn::Error

string literals must not contain more than one class (hint:

Error message

string literals must not contain more than one class (hint: use `{fix}`)

What it means

`classes!` builds a `yew::html::Classes` value from comma-separated arguments. When an argument is a plain string literal, the macro splits it on whitespace and rejects it if it yields more than one class (classes/mod.rs:52-64), printing a hint with the correct comma-separated form. The check applies only to literals — arbitrary expressions pass through to the runtime `Classes::push`, which does accept multi-class strings.

Source

Thrown at packages/yew-macro/src/classes/mod.rs:64

    fn parse(input: ParseStream) -> syn::Result<Self> {
        match input.parse()? {
            Expr::Lit(ExprLit {
                lit: Lit::Str(lit_str),
                ..
            }) => {
                let value = lit_str.value();
                let classes = value.split_whitespace().collect::<Vec<_>>();
                if classes.len() > 1 {
                    let fix = classes
                        .into_iter()
                        .map(|class| format!("\"{class}\""))
                        .collect::<Vec<_>>()
                        .join(", ");
                    let msg = format!(
                        "string literals must not contain more than one class (hint: use `{fix}`)"
                    );

                    Err(syn::Error::new(lit_str.span(), msg))
                } else {
                    Ok(Self::Lit(lit_str))
                }
            }
            expr => Ok(Self::Expr(Box::new(expr))),
        }
    }
}

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Split the literal into comma-separated arguments exactly as the hint shows: `classes!("btn", "btn-primary")`
  2. Keep static and dynamic parts separate: `classes!("base", extra)` where `extra` is a runtime value
  3. If the whole list is genuinely one runtime string, pass it as an expression (e.g. a variable or `format!(...)`) so only the runtime path handles it

Example fix

// before
html! { <div class=classes!("btn btn-primary")>{ "Save" }</div> }

// after
html! { <div class=classes!("btn", "btn-primary")>{ "Save" }</div> }
Defensive patterns

Strategy: validation

Validate before calling

// When class names arrive as one runtime string, build Classes outside the
// literal path — runtime pushes accept multi-class strings:
let classes = yew::html::Classes::from("btn btn-primary"); // splits on whitespace

Prevention

When it happens

Trigger: `classes!("btn btn-primary")` or `class=classes!("a b")` inside `html!` — any single string-literal argument containing two or more whitespace-separated class names.

Common situations: Pasting `class="btn btn-primary"` from HTML/JSX templates or Tailwind class lists into `classes!`, or merging several single-class literals into one string during cleanup.

Related errors


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