Skip to content

Dynamic Prop Labels#3509

Merged
Madoshakalaka merged 13 commits intoyewstack:masterfrom
kirillsemyonkin:dyn-prop
Apr 5, 2026
Merged

Dynamic Prop Labels#3509
Madoshakalaka merged 13 commits intoyewstack:masterfrom
kirillsemyonkin:dyn-prop

Conversation

@kirillsemyonkin
Copy link
Copy Markdown
Contributor

@kirillsemyonkin kirillsemyonkin commented Nov 1, 2023

Description

Partially implements features from discussion #3477. This PR adds Dynamic Prop Labels, so that anyone can write their favorite attributes like HTMX's hx-on:click:

html! {
    <div "hx-on:click"="alert('Clicked!')">{ "Click" }</div>
    <div { "hx-on:click" }={ "alert('Clicked!')" }>{ "Click" }</div>
}

It works by using new PropLabel enum everywhere, which can be either a Static HtmlDashedName like before, or Dynamic Expr. When parsing, if it meets = token after the expression group, it will read a value following it instead of assuming that it is shorthand syntax.

Checklist

  • I have reviewed my own code - what does that mean? Is this always checked for everyone?
  • I have added tests

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2023

Visit the preview URL for this PR (updated for commit 6240128):

https://yew-rs--pr3509-dyn-prop-v9qsw7el.web.app

(expires Sun, 12 Apr 2026 02:00:32 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

@kirillsemyonkin
Copy link
Copy Markdown
Contributor Author

kirillsemyonkin commented Nov 1, 2023

My local setup told me nothing about which Rust version is used, wow...

Fixing 1.64.0 compiler support shortly I guess, there is nothing special, just Result::is_ok_and.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2023

Size Comparison

Details
examples master (KB) pull request (KB) diff (KB) diff (%)
async_clock 99.941 99.941 0 0.000%
boids 167.609 167.609 0 0.000%
communication_child_to_parent 93.235 93.235 0 0.000%
communication_grandchild_with_grandparent 105.061 105.061 0 0.000%
communication_grandparent_to_grandchild 101.410 101.410 0 0.000%
communication_parent_to_child 90.646 90.646 0 0.000%
contexts 105.110 105.110 0 0.000%
counter 85.941 85.941 0 0.000%
counter_functional 87.976 87.976 0 0.000%
dyn_create_destroy_apps 89.865 89.865 0 0.000%
file_upload 98.951 98.951 0 0.000%
function_delayed_input 93.924 93.924 0 0.000%
function_memory_game 172.830 172.830 0 0.000%
function_router 396.243 396.243 0 0.000%
function_todomvc 164.054 164.054 0 0.000%
futures 234.664 234.664 0 0.000%
game_of_life 104.172 104.172 0 0.000%
immutable 257.882 257.882 0 0.000%
inner_html 80.464 80.464 0 0.000%
js_callback 109.086 109.086 0 0.000%
keyed_list 179.568 179.568 0 0.000%
mount_point 83.833 83.833 0 0.000%
nested_list 112.744 112.744 0 0.000%
node_refs 91.222 91.222 0 0.000%
password_strength 1718.404 1718.404 0 0.000%
portals 92.711 92.711 0 0.000%
router 367.169 367.169 0 0.000%
suspense 113.058 113.058 0 0.000%
timer 88.079 88.079 0 0.000%
timer_functional 98.566 98.566 0 0.000%
todomvc 141.760 141.760 0 0.000%
two_apps 85.805 85.805 0 0.000%
web_worker_fib 135.562 135.562 0 0.000%
web_worker_prime 183.950 183.950 0 0.000%
webgl 82.605 82.605 0 0.000%

✅ None of the examples has changed their size significantly.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2023

Benchmark - SSR

Yew Master

Details
Benchmark Round Min (ms) Max (ms) Mean (ms) Standard Deviation
Baseline 10 291.386 292.360 291.762 0.343
Hello World 10 497.092 532.564 515.727 12.709
Function Router 10 31267.667 32485.351 31982.221 391.193
Concurrent Task 10 1006.935 1008.290 1007.519 0.381
Many Providers 10 1096.211 1196.809 1152.681 29.431

Pull Request

Details
Benchmark Round Min (ms) Max (ms) Mean (ms) Standard Deviation
Baseline 10 291.372 291.873 291.540 0.158
Hello World 10 505.155 565.163 524.877 19.045
Function Router 10 32206.196 32988.900 32449.800 279.032
Concurrent Task 10 1006.155 1008.149 1007.133 0.671
Many Providers 10 1121.337 1232.394 1175.342 35.107

@kirillsemyonkin
Copy link
Copy Markdown
Contributor Author

Uh sorry, will check what it fails on tomorrow

@kirillsemyonkin
Copy link
Copy Markdown
Contributor Author

Wow, I passed even the benchmark lol
I still need help with writing tests for this.

@ranile
Copy link
Copy Markdown
Member

ranile commented Nov 2, 2023

You need both passing and failing tests for it. You can copy a -fail.rs and -pass.rs from yew-macro/tests/html_macro and modify it as needed. To regenerate stderr files, you can run tests with TRYBUILD=overwrite environment variable

@kirillsemyonkin
Copy link
Copy Markdown
Contributor Author

If there are any // FIXMEs in my PR, I also need them reviewed

cecton
cecton previously approved these changes Nov 6, 2023
Copy link
Copy Markdown
Contributor

@cecton cecton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! I need this. At the moment I'm building the component linearGradient manually and it's such a pain

#[function_component(LinearGradient)]
pub fn linear_gradient(
    Props {
        id,
        gradient_transform,
        children,
    }: &Props,
) -> Html {
    let mut vtag = yew::virtual_dom::VTag::new("linearGradient");
    vtag.add_children(children.clone());
    vtag.add_attribute("id", id.clone());
    if let Some(x) = gradient_transform.clone() {
        vtag.add_attribute("gradientTransform", x);
    }
    vtag.into()
}

Now with autoprops + dyn prop labels, I can simplify this code a lot

Comment thread packages/yew-macro/tests/html_macro/dyn-prop-fail.stderr Outdated
Comment thread packages/yew-macro/tests/html_macro/dyn-prop-pass.rs
@kirillsemyonkin
Copy link
Copy Markdown
Contributor Author

kirillsemyonkin commented Nov 6, 2023

I tried to add following to the dyn-prop-pass.rs test:

    // property literal
    _ = ::yew::html! { <span ~"hx-on:click"="alert('Clicked!')" /> };
    _ = ::yew::html! { <span ~"hx-on:click"="alert('Clicked!')" ~"hx-on:click"="alert('Clicked!')" /> };
    _ = ::yew::html! { <span ~{ "hx-on:click" }={ "alert('Clicked!')" } /> };
    _ = ::yew::html! { <span ~{ "hx-on:click" }={ "alert('Clicked!')" } ~{ "hx-on:click" }={ "alert('Clicked!')" } /> };

    // property expr
    _ = ::yew::html! { <span ~{ dyn_prop() }={ "alert('Clicked!')" } /> };
    _ = ::yew::html! { <span ~{ dyn_prop() }={ "alert('Clicked!')" } ~{ dyn_prop() }={ "alert('Clicked!')" } /> };

Tests (RUST_BACKTRACE=1 cargo +1.64.0 test -p yew-macro) greeted me with the following:

test tests\html_macro\dyn-prop-pass.rs [should pass] ... error
Test case failed at runtime.

STDERR:
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
thread 'main' panicked at 'function not implemented on non-wasm32 targets', D:\RustDownloads\cargo\registry\src\github.com-1ecc6299db9ec823\wasm-bindgen-0.2.87\src\lib.rs:996:1       
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library\std\src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library\core\src/panicking.rs:142:14
   2: core::panicking::panic
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library\core\src/panicking.rs:48:5
   3: wasm_bindgen::__wbindgen_string_new
             at D:\RustDownloads\cargo\registry\src\github.com-1ecc6299db9ec823\wasm-bindgen-0.2.87\src\lib.rs:41:17
   4: wasm_bindgen::JsValue::from_str
             at D:\RustDownloads\cargo\registry\src\github.com-1ecc6299db9ec823\wasm-bindgen-0.2.87\src\lib.rs:132:32
   5: <wasm_bindgen::JsValue as core::convert::From<&str>>::from
             at D:\RustDownloads\cargo\registry\src\github.com-1ecc6299db9ec823\wasm-bindgen-0.2.87\src\lib.rs:776:9
   6: <T as core::convert::Into<U>>::into
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52\library\core\src\convert/mod.rs:550:9
   7: trybuild005::main
             at D:\RustProjects\yew\packages\yew-macro\tests\html_macro\dyn-prop-pass.rs:58:30
   8: core::ops::function::FnOnce::call_once
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52\library\core\src\ops/function.rs:248:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈

As I was trying to find out whether any tests would help me figure out how to test the feature, I performed a quick search on all tests, only to find out that nobody tested ~ properties. I will await on someone testing and fixing the feature before adding tests like this. (Edit: This is not blocking)

@its-the-shrimp
Copy link
Copy Markdown
Contributor

I performed a quick search on all tests, only to find out that nobody tested ~ properties

Either this is an outdated comment or you missed something, because there are Wasm tests that use ~, if you want to test how ~ works with dynamic prop labels, you can check out one of those Wasm tests and replace ~prop with ~"prop" or ~{ "prop" }. You can find all places where ~ is used by grepping ~[a-zA-Z]

Comment thread packages/yew-macro/src/props/prop.rs Outdated
Comment thread packages/yew-macro/src/html_tree/html_element.rs Outdated
@Madoshakalaka Madoshakalaka added the A-yew-macro Area: The yew-macro crate label Apr 5, 2026
kirillsemyonkin and others added 13 commits April 5, 2026 10:15
Allows not only literals, e.g. "hx-on:click", but all expressions
Avoid temporary TokenStream allocation in ToTokens impls for PropLabel
and Key by delegating directly. Fix Display requirement on PropLabel in
component.rs after rebase brought in is_none_expr. Remove resolved FIXME
on IndexMap filter_map. Update dyn-prop-fail.stderr for Rust 1.84.
Add "Dynamic attribute names" section to elements.mdx and a brief
mention with link in introduction.mdx. Includes translations for
ja, zh-Hans, and zh-Hant locales.
Copy link
Copy Markdown
Member

@Madoshakalaka Madoshakalaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I rebased and addressed Shrimp's reviews. Added docs to the website too.

@Madoshakalaka Madoshakalaka merged commit 92a3d7c into yewstack:master Apr 5, 2026
36 checks passed
shan-shaji pushed a commit to shan-shaji/yew that referenced this pull request Apr 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-yew-macro Area: The yew-macro crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants