From 2b996c2d215a4f1465ee2d00fa787428b4002ea5 Mon Sep 17 00:00:00 2001 From: "Matt \"Siyuan\" Yan" Date: Thu, 26 Feb 2026 15:46:06 +0900 Subject: [PATCH 1/3] fix: allow Option and Option as VNode children in html! Generalize `IntoPropValue for Option` into a blanket `IntoPropValue for Option` where `T: IntoPropValue`. The single-expression child path in the html! macro calls `IntoPropValue::::into_prop_value()` directly, which only had an impl for `Option`. This is a regression from the removal of the `ToHtml` trait, which had a blanket `Option` impl. --- Cargo.lock | 8 ++++---- .../tests/html_macro/element-fail.stderr | 6 +++--- .../tests/html_macro/html-element-pass.rs | 11 +++++++++++ .../yew/src/html/conversion/into_prop_value.rs | 16 ++++++++++++++-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcc295a44b6..ce4fed5cafc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -761,7 +761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -2594,7 +2594,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -3025,7 +3025,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -3779,7 +3779,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/packages/yew-macro/tests/html_macro/element-fail.stderr b/packages/yew-macro/tests/html_macro/element-fail.stderr index 0778bb33660..bafb7c0296d 100644 --- a/packages/yew-macro/tests/html_macro/element-fail.stderr +++ b/packages/yew-macro/tests/html_macro/element-fail.stderr @@ -491,8 +491,8 @@ error[E0277]: the trait bound `Option: IntoPropValue` implements `IntoPropValue>>` `Option>` implements `IntoPropValue>` `Option` implements `IntoPropValue>` + `Option` implements `IntoPropValue` `Option>` implements `IntoPropValue>>` - `Option` implements `IntoPropValue` error[E0277]: the trait bound `Option<{integer}>: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:48:22 @@ -510,8 +510,8 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue` implements `IntoPropValue>>` `Option>` implements `IntoPropValue>` `Option` implements `IntoPropValue>` + `Option` implements `IntoPropValue` `Option>` implements `IntoPropValue>>` - `Option` implements `IntoPropValue` error[E0277]: the trait bound `{integer}: IntoEventCallback` is not satisfied --> tests/html_macro/element-fail.rs:51:28 @@ -627,8 +627,8 @@ error[E0277]: the trait bound `Option: IntoPropValue `Option` implements `IntoPropValue>>` `Option>` implements `IntoPropValue>` `Option` implements `IntoPropValue>` + `Option` implements `IntoPropValue` `Option>` implements `IntoPropValue>>` - `Option` implements `IntoPropValue` error[E0277]: the trait bound `yew::Callback: IntoEventCallback` is not satisfied --> tests/html_macro/element-fail.rs:58:29 diff --git a/packages/yew-macro/tests/html_macro/html-element-pass.rs b/packages/yew-macro/tests/html_macro/html-element-pass.rs index 5c05322b050..65ebc1b642b 100644 --- a/packages/yew-macro/tests/html_macro/html-element-pass.rs +++ b/packages/yew-macro/tests/html_macro/html-element-pass.rs @@ -123,6 +123,17 @@ fn compile_pass() { let option_vnode = ::std::option::Option::Some(::yew::html! {}); _ = ::yew::html! {
{option_vnode}
}; + + let opt_string: ::std::option::Option<::std::string::String> = + ::std::option::Option::Some(::std::convert::From::from("hello")); + _ = ::yew::html! {
{opt_string}
}; + + let opt_attr: ::std::option::Option<::yew::AttrValue> = + ::std::option::Option::Some(::yew::AttrValue::Static("hello")); + _ = ::yew::html! {
{opt_attr}
}; + + let opt_none: ::std::option::Option<::std::string::String> = ::std::option::Option::None; + _ = ::yew::html! {
{opt_none}
}; } fn main() {} diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 579660920dd..efefe635e25 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -214,10 +214,10 @@ impl IntoPropValue for Vec { } } -impl IntoPropValue for Option { +impl> IntoPropValue for Option { #[inline] fn into_prop_value(self) -> VNode { - self.unwrap_or_default() + self.map(IntoPropValue::into_prop_value).unwrap_or_default() } } @@ -350,6 +350,18 @@ mod test { let _: Option = Cow::Borrowed("foo").into_prop_value(); } + #[test] + fn test_option_to_vnode() { + let _: VNode = Some(String::from("hello")).into_prop_value(); + let _: VNode = Some(AttrValue::Static("hello")).into_prop_value(); + let _: VNode = Option::::None.into_prop_value(); + let _: VNode = Option::::None.into_prop_value(); + let _: VNode = Some(VNode::default()).into_prop_value(); + let _: VNode = Option::::None.into_prop_value(); + let _: VNode = Some(42u32).into_prop_value(); + let _: VNode = Some(true).into_prop_value(); + } + #[test] fn test_callback() { let _: Callback = (|_: String| ()).into_prop_value(); From 6331a01ec0415c2be6bc912e8c6e42616213c3c0 Mon Sep 17 00:00:00 2001 From: "Matt \"Siyuan\" Yan" Date: Thu, 26 Feb 2026 16:07:39 +0900 Subject: [PATCH 2/3] fix: allow &T references as VNode children in html! macro Add IntoPropValue impls for &T where T already has an impl, via the impl_into_prop_value_via_display! and impl_into_prop_value_via_attr_value! macros. This fixes a regression from the ToHtml removal where expressions like {&num} or {element} (where element: &i32 from .iter()) failed as single children of HTML elements. --- .../tests/html_macro/component-fail.stderr | 38 +++++++++---------- .../tests/html_macro/element-fail.stderr | 8 ++-- .../tests/html_macro/html-element-pass.rs | 9 +++++ .../src/html/conversion/into_prop_value.rs | 19 ++++++++++ 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/packages/yew-macro/tests/html_macro/component-fail.stderr b/packages/yew-macro/tests/html_macro/component-fail.stderr index 941e358d815..8f34088e4ee 100644 --- a/packages/yew-macro/tests/html_macro/component-fail.stderr +++ b/packages/yew-macro/tests/html_macro/component-fail.stderr @@ -488,14 +488,14 @@ error[E0277]: the trait bound `{integer}: IntoPropValue` is not satisfie | required by a bound introduced by this call | = help: the following other types implement trait `IntoPropValue`: - f32 - f64 - i128 - i16 - i32 - i64 - i8 - isize + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + &i8 + &isize and $N others note: required by a bound in `ChildPropertiesBuilder::string` --> tests/html_macro/component-fail.rs:4:17 @@ -516,14 +516,14 @@ error[E0277]: the trait bound `{integer}: IntoPropValue` is not satisfie | required by a bound introduced by this call | = help: the following other types implement trait `IntoPropValue`: - f32 - f64 - i128 - i16 - i32 - i64 - i8 - isize + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + &i8 + &isize and $N others note: required by a bound in `ChildPropertiesBuilder::string` --> tests/html_macro/component-fail.rs:4:17 @@ -560,9 +560,9 @@ error[E0277]: the trait bound `u32: IntoPropValue` is not satisfied | | | required by a bound introduced by this call | - = help: the trait `IntoPropValue` is not implemented for `u32` - but trait `IntoPropValue` is implemented for it - = help: for that trait implementation, expected `VNode`, found `i32` + = help: the following other types implement trait `IntoPropValue`: + &u32 + u32 note: required by a bound in `ChildPropertiesBuilder::int` --> tests/html_macro/component-fail.rs:4:17 | diff --git a/packages/yew-macro/tests/html_macro/element-fail.stderr b/packages/yew-macro/tests/html_macro/element-fail.stderr index bafb7c0296d..cd2a87bb422 100644 --- a/packages/yew-macro/tests/html_macro/element-fail.stderr +++ b/packages/yew-macro/tests/html_macro/element-fail.stderr @@ -465,14 +465,14 @@ error[E0277]: the trait bound `NotToString: IntoPropValue>` is not implemented for `NotToString` | = help: the following other types implement trait `IntoPropValue`: + `&&String` implements `IntoPropValue` + `&&str` implements `IntoPropValue` `&'static [(K, V)]` implements `IntoPropValue>` `&'static [T]` implements `IntoPropValue>` `&'static str` implements `IntoPropValue` `&'static str` implements `IntoPropValue>` `&'static str` implements `IntoPropValue>` `&'static str` implements `IntoPropValue` - `&'static str` implements `IntoPropValue` - `&ChildrenRenderer` implements `IntoPropValue` and $N others error[E0277]: the trait bound `Option: IntoPropValue>` is not satisfied @@ -665,14 +665,14 @@ error[E0277]: the trait bound `NotToString: IntoPropValue>` is not implemented for `NotToString` | = help: the following other types implement trait `IntoPropValue`: + `&&String` implements `IntoPropValue` + `&&str` implements `IntoPropValue` `&'static [(K, V)]` implements `IntoPropValue>` `&'static [T]` implements `IntoPropValue>` `&'static str` implements `IntoPropValue` `&'static str` implements `IntoPropValue>` `&'static str` implements `IntoPropValue>` `&'static str` implements `IntoPropValue` - `&'static str` implements `IntoPropValue` - `&ChildrenRenderer` implements `IntoPropValue` and $N others error[E0277]: the trait bound `(): IntoPropValue` is not satisfied diff --git a/packages/yew-macro/tests/html_macro/html-element-pass.rs b/packages/yew-macro/tests/html_macro/html-element-pass.rs index 65ebc1b642b..5d1a1a4fe52 100644 --- a/packages/yew-macro/tests/html_macro/html-element-pass.rs +++ b/packages/yew-macro/tests/html_macro/html-element-pass.rs @@ -134,6 +134,15 @@ fn compile_pass() { let opt_none: ::std::option::Option<::std::string::String> = ::std::option::Option::None; _ = ::yew::html! {
{opt_none}
}; + + let num = 42i32; + _ = ::yew::html! {
{&num}
}; + + let text = ::std::string::String::new(); + _ = ::yew::html! {
{&text}
}; + + let sr: &::core::primitive::str = "hello"; + _ = ::yew::html! {
{&sr}
}; } fn main() {} diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index efefe635e25..343ef2a28d4 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -292,6 +292,12 @@ macro_rules! impl_into_prop_value_via_display { VText::from(self).into() } } + impl IntoPropValue for &$from_ty { + #[inline(always)] + fn into_prop_value(self) -> VNode { + self.clone().into_prop_value() + } + } }; } @@ -362,6 +368,19 @@ mod test { let _: VNode = Some(true).into_prop_value(); } + #[test] + fn test_ref_to_vnode() { + let _: VNode = (&42i32).into_prop_value(); + let _: VNode = (&true).into_prop_value(); + let _: VNode = (&1.5f64).into_prop_value(); + let s = String::from("hello"); + let _: VNode = (&s).into_prop_value(); + let a = AttrValue::Static("hello"); + let _: VNode = (&a).into_prop_value(); + let sr: &str = "hello"; + let _: VNode = (&sr).into_prop_value(); + } + #[test] fn test_callback() { let _: Callback = (|_: String| ()).into_prop_value(); From cdb69931521dd44f0f15635635f85cc46f7aaf9e Mon Sep 17 00:00:00 2001 From: "Matt \"Siyuan\" Yan" Date: Thu, 26 Feb 2026 16:22:00 +0900 Subject: [PATCH 3/3] fix: website snippet affected by the regression --- website/docs/advanced-topics/children.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/advanced-topics/children.mdx b/website/docs/advanced-topics/children.mdx index 8966e1d95ff..868cab2692b 100644 --- a/website/docs/advanced-topics/children.mdx +++ b/website/docs/advanced-topics/children.mdx @@ -287,7 +287,7 @@ impl Component for Page { fn view(&self, ctx: &Context) -> Html { html! {
- { ctx.props().sidebar.clone().map(Html::from).unwrap_or_default() } + { ctx.props().sidebar.clone() } // ... page content
}