Ver Fonte

Disambiguate expressions in rsx by requiring curlies

Jonathan Kelley há 1 ano atrás
pai
commit
d9b84f9f8f
53 ficheiros alterados com 401 adições e 375 exclusões
  1. 5 1
      examples/all_events.rs
  2. 4 4
      examples/calculator.rs
  3. 2 2
      examples/compose.rs
  4. 1 1
      examples/control_focus.rs
  5. 2 2
      examples/crm.rs
  6. 2 2
      examples/drops.rs
  7. 3 3
      examples/fermi.rs
  8. 9 11
      examples/file_explorer.rs
  9. 6 5
      examples/framework_benchmark.rs
  10. 3 3
      examples/inputs.rs
  11. 5 5
      examples/openid_connect_demo/src/views/header.rs
  12. 5 2
      examples/openid_connect_demo/src/views/not_found.rs
  13. 5 5
      examples/pattern_model.rs
  14. 1 1
      examples/pattern_reducer.rs
  15. 2 2
      examples/query_segments_demo/src/main.rs
  16. 8 8
      examples/rsx_compile_fail.rs
  17. 17 16
      examples/rsx_usage.rs
  18. 1 1
      examples/scroll_to_top.rs
  19. 3 3
      examples/signals.rs
  20. 5 5
      examples/simple_list.rs
  21. 1 1
      examples/svg.rs
  22. 33 35
      examples/todomvc.rs
  23. 5 4
      packages/autofmt/src/writer.rs
  24. 4 4
      packages/core/tests/create_dom.rs
  25. 9 9
      packages/core/tests/create_fragments.rs
  26. 2 2
      packages/core/tests/create_lists.rs
  27. 2 2
      packages/core/tests/create_passthru.rs
  28. 1 1
      packages/core/tests/diff_component.rs
  29. 12 12
      packages/core/tests/diff_keyed_list.rs
  30. 15 13
      packages/core/tests/diff_unkeyed_list.rs
  31. 2 2
      packages/core/tests/event_propagation.rs
  32. 1 1
      packages/core/tests/lifecycle.rs
  33. 2 2
      packages/core/tests/miri_full_app.rs
  34. 2 2
      packages/core/tests/miri_stress.rs
  35. 1 1
      packages/core/tests/task.rs
  36. 1 1
      packages/desktop/headless_tests/rendering.rs
  37. 1 1
      packages/dioxus-tui/examples/all_terminal_events.rs
  38. 23 33
      packages/dioxus-tui/examples/buttons.rs
  39. 15 22
      packages/dioxus-tui/examples/color_test.rs
  40. 2 2
      packages/dioxus-tui/examples/list.rs
  41. 18 22
      packages/dioxus-tui/examples/many_small_edit_stress.rs
  42. 6 4
      packages/dioxus/examples/stress.rs
  43. 11 26
      packages/native-core/src/utils/persistant_iterator.rs
  44. 1 1
      packages/router/examples/simple_routes.rs
  45. 2 2
      packages/router/src/components/history_buttons.rs
  46. 2 2
      packages/router/src/components/link.rs
  47. 117 68
      packages/rsx/src/node.rs
  48. 3 1
      packages/ssr/src/renderer.rs
  49. 3 7
      packages/ssr/tests/hydration.rs
  50. 7 5
      packages/ssr/tests/simple.rs
  51. 2 2
      packages/web/examples/hydrate.rs
  52. 5 2
      packages/web/examples/timeout_count.rs
  53. 1 1
      packages/web/tests/hydrate.rs

+ 5 - 1
examples/all_events.rs

@@ -76,7 +76,11 @@ fn app(cx: Scope) -> Element {
 
                 "Hover, click, type or scroll to see the info down below"
             }
-            div { events.read().iter().map(|event| rsx!( div { "{event:?}" } )) }
+            div {
+                for event in events.read().iter() {
+                    div { "{event:?}" }
+                }
+            }
         }
     ))
 }

+ 4 - 4
examples/calculator.rs

@@ -58,13 +58,13 @@ fn app(cx: Scope) -> Element {
     };
 
     cx.render(rsx!(
-        style { include_str!("./assets/calculator.css") }
+        style { {include_str!("./assets/calculator.css")} }
         div { id: "wrapper",
             div { class: "app",
                 div { class: "calculator",
                     tabindex: "0",
                     onkeydown: handle_key_down_event,
-                    div { class: "calculator-display", val.to_string() }
+                    div { class: "calculator-display", "{val}" }
                     div { class: "calculator-keypad",
                         div { class: "input-keys",
                             div { class: "function-keys",
@@ -103,14 +103,14 @@ fn app(cx: Scope) -> Element {
                             div { class: "digit-keys",
                                 button { class: "calculator-key key-0", onclick: move |_| input_digit(0), "0" }
                                 button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'), "●" }
-                                (1..10).map(|k| rsx!{
+                                for k in 1..10 {
                                     button {
                                         class: "calculator-key {k}",
                                         name: "key-{k}",
                                         onclick: move |_| input_digit(k),
                                         "{k}"
                                     }
-                                }),
+                                }
                             }
                         }
                         div { class: "operator-keys",

+ 2 - 2
examples/compose.rs

@@ -32,12 +32,12 @@ fn app(cx: Scope) -> Element {
             }
 
             ul {
-                emails_sent.read().iter().map(|message| cx.render(rsx! {
+                for message in emails_sent.read().iter() {
                     li {
                         h3 { "email" }
                         span {"{message}"}
                     }
-                }))
+                }
             }
         }
     })

+ 1 - 1
examples/control_focus.rs

@@ -16,7 +16,7 @@ fn app(cx: Scope) -> Element {
             loop {
                 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
                 if let Some(element) = elements.read().get(focused) {
-                    element.set_focus(true);
+                    _ = element.set_focus(true).await;
                 } else {
                     focused = 0;
                 }

+ 2 - 2
examples/crm.rs

@@ -62,7 +62,7 @@ fn ClientList(cx: Scope) -> Element {
         Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" }
         Link { to: Route::Settings {}, class: "pure-button", "Settings" }
 
-        clients.read().iter().map(|client| rsx! {
+        for client in clients.read().iter() {
             div {
                 class: "client",
                 style: "margin-bottom: 50px",
@@ -70,7 +70,7 @@ fn ClientList(cx: Scope) -> Element {
                 p { "Name: {client.first_name} {client.last_name}" }
                 p { "Description: {client.description}" }
             }
-        })
+        }
     })
 }
 

+ 2 - 2
examples/drops.rs

@@ -14,9 +14,9 @@ fn app(cx: Scope) -> Element {
     }
 
     render! {
-        (0..count).map(|_| rsx!{
+        {(0..count).map(|_| rsx!{
             drop_child {}
-        })
+        })}
     }
 }
 

+ 3 - 3
examples/fermi.rs

@@ -39,9 +39,9 @@ fn ChildWithRef(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
             ul {
-                names.read().iter().map(|f| rsx!{
-                    li { "hello: {f}" }
-                })
+                for name in names.read().iter() {
+                    li { "hello: {name}" }
+                }
             }
             button {
                 onclick: move |_| {

+ 9 - 11
examples/file_explorer.rs

@@ -28,12 +28,12 @@ fn app(cx: Scope) -> Element {
             link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", }
             header {
                 i { class: "material-icons icon-menu", "menu" }
-                h1 { "Files: ", files.read().current() }
+                h1 { "Files: ", {files.read().current()} }
                 span { }
                 i { class: "material-icons", onclick: move |_| files.write().go_up(), "logout" }
             }
             main {
-                files.read().path_names.iter().enumerate().map(|(dir_id, path)| {
+                {files.read().path_names.iter().enumerate().map(|(dir_id, path)| {
                     let path_end = path.split('/').last().unwrap_or(path.as_str());
                     let icon_type = if path_end.contains('.') {
                         "description"
@@ -52,15 +52,13 @@ fn app(cx: Scope) -> Element {
                             h1 { "{path_end}" }
                         }
                     )
-                }),
-                files.read().err.as_ref().map(|err| {
-                    rsx! (
-                        div {
-                            code { "{err}" }
-                            button { onclick: move |_| files.write().clear_err(), "x" }
-                        }
-                    )
-                })
+                })},
+                if let Some(err) = files.read().err.as_ref() {
+                    div {
+                        code { "{err}" }
+                        button { onclick: move |_| files.write().clear_err(), "x" }
+                    }
+                }
             }
         }
     })

+ 6 - 5
examples/framework_benchmark.rs

@@ -66,9 +66,9 @@ fn app(cx: Scope) -> Element {
             }
             table {
                 tbody {
-                    items.read().iter().enumerate().map(|(id, item)| {
-                        let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
-                        rsx!(tr { class: "{is_in_danger}",
+                    for (id, item) in items.read().iter().enumerate() {
+                        tr {
+                            class: if (*selected).map(|s| s == id).unwrap_or(false) { "danger" },
                             td { class:"col-md-1" }
                             td { class:"col-md-1", "{item.key}" }
                             td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
@@ -80,8 +80,9 @@ fn app(cx: Scope) -> Element {
                                 }
                             }
                             td { class: "col-md-6" }
-                        })
-                    })
+                        }
+                    }
+
                 }
              }
             span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" }

+ 3 - 3
examples/inputs.rs

@@ -37,7 +37,7 @@ const FIELDS: &[(&str, &str)] = &[
 fn app(cx: Scope) -> Element {
     cx.render(rsx! {
         div { margin_left: "30px",
-            select_example(cx),
+            {select_example(cx)},
             div {
                 // handling inputs on divs will catch all input events below
                 // so the value of our input event will be either huey, dewey, louie, or true/false (because of the checkboxe)
@@ -114,7 +114,7 @@ fn app(cx: Scope) -> Element {
                 }
             }
 
-            FIELDS.iter().map(|(field, value)| rsx! {
+            for (field, value) in FIELDS.iter() {
                 div {
                     input {
                         id: "{field}",
@@ -131,7 +131,7 @@ fn app(cx: Scope) -> Element {
                     }
                     br {}
                 }
-            })
+            }
         }
     })
 }

+ 5 - 5
examples/openid_connect_demo/src/views/header.rs

@@ -41,7 +41,7 @@ pub fn LogOut(cx: Scope<ClientProps>) -> Element {
                         }
                         Err(error) => {
                             rsx! {
-                                div { format!{"Failed to load disconnection url: {:?}", error} }
+                                div { "Failed to load disconnection url: {error:?}" }
                             }
                         }
                     },
@@ -143,9 +143,9 @@ pub fn LoadClient(cx: Scope) -> Element {
                 }
             }
             Err(error) => {
+                log::info! {"Failed to load client: {:?}", error};
                 rsx! {
-                    div { format!{"Failed to load client: {:?}", error} }
-                    log::info!{"Failed to load client: {:?}", error},
+                    div { "Failed to load client: {error:?}" }
                     Outlet::<Route> {}
                 }
             }
@@ -184,7 +184,7 @@ pub fn AuthHeader(cx: Scope) -> Element {
                                 Ok(email) => {
                                     rsx! {
                                         div {
-                                            div { email }
+                                            div { {email} }
                                             LogOut { client_id: client_props.client_id, client: client_props.client }
                                             Outlet::<Route> {}
                                         }
@@ -207,7 +207,7 @@ pub fn AuthHeader(cx: Scope) -> Element {
                                         log::info!("Other issue with token");
                                         rsx! {
                                             div {
-                                                div { error.to_string() }
+                                                div { "{error}" }
                                                 Outlet::<Route> {}
                                             }
                                         }

+ 5 - 2
examples/openid_connect_demo/src/views/not_found.rs

@@ -2,6 +2,9 @@ use dioxus::prelude::*;
 
 #[component]
 pub fn NotFound(cx: Scope, route: Vec<String>) -> Element {
-    let routes = route.join("");
-    render! {rsx! {div{routes}}}
+    render! {
+        div{
+            {route.join("")}
+        }
+    }
 }

+ 5 - 5
examples/pattern_model.rs

@@ -39,11 +39,11 @@ fn app(cx: Scope) -> Element {
     let state = use_ref(cx, Calculator::new);
 
     cx.render(rsx! {
-        style { include_str!("./assets/calculator.css") }
+        style { {include_str!("./assets/calculator.css")} }
         div { id: "wrapper",
             div { class: "app",
                 div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
-                    div { class: "calculator-display", state.read().formatted_display() }
+                    div { class: "calculator-display", {state.read().formatted_display()} }
                     div { class: "calculator-keypad",
                         div { class: "input-keys",
                             div { class: "function-keys",
@@ -74,14 +74,14 @@ fn app(cx: Scope) -> Element {
                                     onclick: move |_|  state.write().input_dot(),
                                     "●"
                                 }
-                                (1..10).map(move |k| rsx!{
+                                for k in 1..10 {
                                     CalculatorKey {
                                         key: "{k}",
                                         name: "key-{k}",
                                         onclick: move |_| state.write().input_digit(k),
                                         "{k}"
                                     }
-                                })
+                                }
                             }
                         }
                         div { class: "operator-keys",
@@ -130,7 +130,7 @@ fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
         button {
             class: "calculator-key {cx.props.name}",
             onclick: move |e| cx.props.onclick.call(e),
-            &cx.props.children
+            {&cx.props.children}
         }
     })
 }

+ 1 - 1
examples/pattern_reducer.rs

@@ -20,7 +20,7 @@ fn app(cx: Scope) -> Element {
     cx.render(rsx!(
         div {
             h1 {"Select an option"}
-            h3 { "The radio is... ", state.is_playing(), "!" }
+            h3 { "The radio is... ", {state.is_playing()}, "!" }
             button { onclick: move |_| state.make_mut().reduce(PlayerAction::Pause),
                 "Pause"
             }

+ 2 - 2
examples/query_segments_demo/src/main.rs

@@ -66,7 +66,7 @@ impl FromQuery for ManualBlogQuerySegments {
 fn BlogPost(cx: Scope, query_params: ManualBlogQuerySegments) -> Element {
     render! {
         div{"This is your blogpost with a query segment:"}
-        div{format!("{:?}", query_params)}
+        div{ "{query_params:?}" }
     }
 }
 
@@ -74,7 +74,7 @@ fn BlogPost(cx: Scope, query_params: ManualBlogQuerySegments) -> Element {
 fn AutomaticBlogPost(cx: Scope, name: String, surname: String) -> Element {
     render! {
         div{"This is your blogpost with a query segment:"}
-        div{format!("name={}&surname={}", name, surname)}
+        div{ "name={name}&surname={surname}" }
     }
 }
 

+ 8 - 8
examples/rsx_compile_fail.rs

@@ -36,17 +36,17 @@ fn example(cx: Scope) -> Element {
             div { id: "asd",
                 "your neighborhood spiderman"
 
-                items.iter().cycle().take(5).map(|f| rsx!{
-                    div { "{f.a}" }
-                })
+                for item in items.iter().cycle().take(5) {
+                    div { "{item.a}" }
+                }
 
-                things_list.iter().map(|f| rsx!{
-                    div { "{f.a}" "{f.b}" }
-                })
+                for thing in things_list.iter() {
+                    div { "{thing.a}" "{thing.b}" }
+                }
 
-                mything_read.as_ref().map(|f| rsx! {
+                if let Some(f) = mything_read.as_ref() {
                     div { "{f}" }
-                })
+                }
             }
         }
     ))

+ 17 - 16
examples/rsx_usage.rs

@@ -61,7 +61,7 @@ fn App(cx: Scope) -> Element {
             h1 {"Some text"}
             h1 {"Some text with {formatting}"}
             h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
-            h1 {"Formatting without interpolation " formatting_tuple.0 "and" formatting_tuple.1 }
+            h1 {"Formatting without interpolation " {formatting_tuple.0} "and" {formatting_tuple.1} }
             h2 {
                 "Multiple"
                 "Text"
@@ -94,10 +94,10 @@ fn App(cx: Scope) -> Element {
             }
 
             // Expressions can be used in element position too:
-            rsx!(p { "More templating!" }),
+            {rsx!(p { "More templating!" })},
 
             // Iterators
-            (0..10).map(|i| rsx!(li { "{i}" })),
+            {(0..10).map(|i| rsx!(li { "{i}" }))},
 
             // Iterators within expressions
             {
@@ -117,24 +117,25 @@ fn App(cx: Scope) -> Element {
             // Conditional rendering
             // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
             // You can convert a bool condition to rsx! with .then and .or
-            true.then(|| rsx!(div {})),
+            {true.then(|| rsx!(div {}))},
 
             // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element
             if false {
-                rsx!(h1 {"Top text"})
+                h1 {"Top text"}
             } else {
-                rsx!(h1 {"Bottom text"})
+                h1 {"Bottom text"}
             }
 
             // Using optionals for diverging branches
-            if true {
+            // Note that since this is wrapped in curlies, it's interpreted as an expression
+            {if true {
                 Some(rsx!(h1 {"Top text"}))
             } else {
                 None
-            }
+            }}
 
             // returning "None" without a diverging branch is a bit noisy... but rare in practice
-            None as Option<()>,
+            {None as Option<()>},
 
             // can also just use empty fragments
             Fragment {}
@@ -169,13 +170,13 @@ fn App(cx: Scope) -> Element {
 
             // Can pass in props directly as an expression
             {
-                let props = TallerProps {a: "hello", children: cx.render(rsx!(()))};
+                let props = TallerProps {a: "hello", children: None };
                 rsx!(Taller { ..props })
             }
 
             // Spreading can also be overridden manually
             Taller {
-                ..TallerProps { a: "ballin!", children: cx.render(rsx!(()) )},
+                ..TallerProps { a: "ballin!", children: None },
                 a: "not ballin!"
             }
 
@@ -204,16 +205,16 @@ fn App(cx: Scope) -> Element {
 
             // helper functions
             // Anything that implements IntoVnode can be dropped directly into Rsx
-            helper(cx, "hello world!")
+            {helper(cx, "hello world!")}
 
             // Strings can be supplied directly
-            String::from("Hello world!")
+            {String::from("Hello world!")}
 
             // So can format_args
-            format_args!("Hello {}!", "world")
+            {format_args!("Hello {}!", "world")}
 
             // Or we can shell out to a helper function
-            format_dollars(10, 50)
+            {format_dollars(10, 50)}
         }
     })
 }
@@ -269,7 +270,7 @@ pub struct TallerProps<'a> {
 #[component]
 pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element {
     cx.render(rsx! {
-        &cx.props.children
+        {&cx.props.children}
     })
 }
 

+ 1 - 1
examples/scroll_to_top.rs

@@ -23,7 +23,7 @@ fn app(cx: Scope) -> Element {
             button {
                 onclick: move |_| {
                     if let Some(header) = header_element.read().as_ref() {
-                        header.scroll_to(ScrollBehavior::Smooth);
+                        _ = header.scroll_to(ScrollBehavior::Smooth);
                     }
                 },
                 "Scroll to top"

+ 3 - 3
examples/signals.rs

@@ -31,7 +31,7 @@ fn app(cx: Scope) -> Element {
 
         // We can do boolean operations on the current signal value
         if count.value() > 5 {
-            rsx!{ h2 { "High five!" } }
+            h2 { "High five!" }
         }
 
         // We can cleanly map signals with iterators
@@ -41,9 +41,9 @@ fn app(cx: Scope) -> Element {
 
         // We can also use the signal value as a slice
         if let [ref first, .., ref last] = saved_values.read().as_slice() {
-            rsx! { li { "First and last: {first}, {last}" } }
+            li { "First and last: {first}, {last}" }
         } else {
-            rsx! { "No saved values" }
+            "No saved values"
         }
     })
 }

+ 5 - 5
examples/simple_list.rs

@@ -8,17 +8,17 @@ fn app(cx: Scope) -> Element {
     cx.render(rsx!(
         div {
             // Use Map directly to lazily pull elements
-            (0..10).map(|f| rsx! { "{f}" }),
+            {(0..10).map(|f| rsx! { "{f}" })},
 
             // Collect into an intermediate collection if necessary, and call into_iter
-            ["a", "b", "c", "d", "e", "f"]
+            {["a", "b", "c", "d", "e", "f"]
                 .into_iter()
                 .map(|f| rsx! { "{f}" })
                 .collect::<Vec<_>>()
-                .into_iter(),
+                .into_iter()},
 
             // Use optionals
-            Some(rsx! { "Some" }),
+            {Some(rsx! { "Some" })},
 
             // use a for loop where the body itself is RSX
             for name in 0..10 {
@@ -27,7 +27,7 @@ fn app(cx: Scope) -> Element {
 
             // Or even use an unterminated conditional
             if true {
-                rsx!{ "hello world!" }
+                "hello world!"
             }
         }
     ))

+ 1 - 1
examples/svg.rs

@@ -97,7 +97,7 @@ pub fn Die<'a>(cx: Scope<'a, DieProps<'a>>) -> Element {
           fill: "{fill}",
         }
 
-        dots
+        {dots}
       }
     })
 }

+ 33 - 35
examples/todomvc.rs

@@ -52,30 +52,30 @@ pub fn app(cx: Scope<()>) -> Element {
             TodoHeader { todos: todos }
             section { class: "main",
                 if !todos.is_empty() {
-                    rsx! {
-                        input {
-                            id: "toggle-all",
-                            class: "toggle-all",
-                            r#type: "checkbox",
-                            onchange: move |_| {
-                                let check = active_todo_count != 0;
-                                for (_, item) in todos.make_mut().iter_mut() {
-                                    item.checked = check;
-                                }
-                            },
-                            checked: if active_todo_count == 0 { "true" } else { "false" },
-                        }
-                        label { r#for: "toggle-all" }
+                    input {
+                        id: "toggle-all",
+                        class: "toggle-all",
+                        r#type: "checkbox",
+                        onchange: move |_| {
+                            let check = active_todo_count != 0;
+                            for (_, item) in todos.make_mut().iter_mut() {
+                                item.checked = check;
+                            }
+                        },
+                        checked: if active_todo_count == 0 { "true" } else { "false" },
                     }
+                    label { r#for: "toggle-all" }
                 }
                 ul { class: "todo-list",
-                    filtered_todos.iter().map(|id| rsx!(TodoEntry {
-                        key: "{id}",
-                        id: *id,
-                        todos: todos,
-                    }))
+                    for id in filtered_todos.iter() {
+                        TodoEntry {
+                            key: "{id}",
+                            id: *id,
+                            todos: todos,
+                        }
+                    }
                 }
-                (!todos.is_empty()).then(|| rsx!(
+                if !todos.is_empty() {
                     ListFooter {
                         active_todo_count: active_todo_count,
                         active_todo_text: active_todo_text,
@@ -83,7 +83,7 @@ pub fn app(cx: Scope<()>) -> Element {
                         todos: todos,
                         filter: filter,
                     }
-                ))
+                }
             }
         }
         PageFooter {}
@@ -172,7 +172,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
                     prevent_default: "onclick"
                 }
             }
-            is_editing.then(|| rsx!{
+            if **is_editing {
                 input {
                     class: "edit",
                     value: "{todo.contents}",
@@ -186,7 +186,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
                         }
                     },
                 }
-            })
+            }
         }
     })
 }
@@ -220,29 +220,27 @@ pub fn ListFooter<'a>(cx: Scope<'a, ListFooterProps<'a>>) -> Element {
             }
             ul { class: "filters",
                 for (state , state_text , url) in [
-    (FilterState::All, "All", "#/"),
-    (FilterState::Active, "Active", "#/active"),
-    (FilterState::Completed, "Completed", "#/completed"),
-] {
+                    (FilterState::All, "All", "#/"),
+                    (FilterState::Active, "Active", "#/active"),
+                    (FilterState::Completed, "Completed", "#/completed"),
+                ] {
                     li {
                         a {
                             href: url,
                             class: selected(state),
                             onclick: move |_| cx.props.filter.set(state),
                             prevent_default: "onclick",
-                            state_text
+                            {state_text}
                         }
                     }
                 }
             }
             if cx.props.show_clear_completed {
-                cx.render(rsx! {
-                    button {
-                        class: "clear-completed",
-                        onclick: move |_| cx.props.todos.make_mut().retain(|_, todo| !todo.checked),
-                        "Clear completed"
-                    }
-                })
+                button {
+                    class: "clear-completed",
+                    onclick: move |_| cx.props.todos.make_mut().retain(|_, todo| !todo.checked),
+                    "Clear completed"
+                }
             }
         }
     })

+ 5 - 4
packages/autofmt/src/writer.rs

@@ -1,11 +1,11 @@
-use dioxus_rsx::{AttributeType, BodyNode, ElementAttrValue, ForLoop};
+use dioxus_rsx::{AttributeType, BodyNode, ElementAttrValue, ForLoop, IfChain};
 use proc_macro2::{LineColumn, Span};
 use quote::ToTokens;
 use std::{
     collections::{HashMap, VecDeque},
     fmt::{Result, Write},
 };
-use syn::{spanned::Spanned, Expr, ExprIf};
+use syn::{spanned::Spanned, Expr};
 
 use crate::buffer::Buffer;
 use crate::ifmt_to_string;
@@ -231,8 +231,9 @@ impl<'a> Writer<'a> {
         Ok(())
     }
 
-    fn write_if_chain(&mut self, ifchain: &ExprIf) -> std::fmt::Result {
-        self.write_raw_expr(ifchain.span())
+    fn write_if_chain(&mut self, ifchain: &IfChain) -> std::fmt::Result {
+        todo!()
+        // self.write_raw_expr(ifchain.span())
     }
 }
 

+ 4 - 4
packages/core/tests/create_dom.rs

@@ -84,7 +84,7 @@ fn create() {
 fn create_list() {
     let mut dom = VirtualDom::new(|cx| {
         cx.render(rsx! {
-            (0..3).map(|f| rsx!( div { "hello" } ))
+            {(0..3).map(|f| rsx!( div { "hello" } ))}
         })
     });
 
@@ -148,7 +148,7 @@ fn create_components() {
     fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
         cx.render(rsx! {
             h1 {}
-            div { &cx.props.children }
+            div { {&cx.props.children} }
             p {}
         })
     }
@@ -163,10 +163,10 @@ fn anchors() {
     let mut dom = VirtualDom::new(|cx| {
         cx.render(rsx! {
             if true {
-                rsx!( div { "hello" } )
+                div { "hello" }
             }
             if false {
-                rsx!( div { "goodbye" } )
+                div { "goodbye" }
             }
         })
     });

+ 9 - 9
packages/core/tests/create_fragments.rs

@@ -7,7 +7,7 @@ use dioxus_core::ElementId;
 #[test]
 fn empty_fragment_creates_nothing() {
     fn app(cx: Scope) -> Element {
-        cx.render(rsx!(()))
+        cx.render(rsx!({ () }))
     }
 
     let mut vdom = VirtualDom::new(app);
@@ -43,18 +43,18 @@ fn fragments_nested() {
         cx.render(rsx!(
             div { "hello" }
             div { "goodbye" }
-            rsx! {
+            {rsx! {
                 div { "hello" }
                 div { "goodbye" }
-                rsx! {
+                {rsx! {
                     div { "hello" }
                     div { "goodbye" }
-                    rsx! {
+                    {rsx! {
                         div { "hello" }
                         div { "goodbye" }
-                    }
-                }
-            }
+                    }}
+                }}
+            }}
         ))
     });
 
@@ -79,7 +79,7 @@ fn fragments_across_components() {
         let world = "world";
         cx.render(rsx! {
             "hellO!"
-            world
+            {world}
         })
     }
 
@@ -94,7 +94,7 @@ fn list_fragments() {
     fn app(cx: Scope) -> Element {
         cx.render(rsx!(
             h1 {"hello"}
-            (0..6).map(|f| rsx!( span { "{f}" }))
+            {(0..6).map(|f| rsx!( span { "{f}" }))}
         ))
     }
     assert_eq!(

+ 2 - 2
packages/core/tests/create_lists.rs

@@ -11,12 +11,12 @@ use dioxus_core::ElementId;
 fn app(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
-            (0..3).map(|i| rsx! {
+            for i in 0..3 {
                 div {
                     h1 { "hello world! "}
                     p { "{i}" }
                 }
-            })
+            }
         }
     })
 }

+ 2 - 2
packages/core/tests/create_passthru.rs

@@ -20,7 +20,7 @@ fn nested_passthru_creates() {
 
     #[component]
     fn PassThru<'a>(cx: Scope<'a>, children: Element<'a>) -> Element {
-        cx.render(rsx!(children))
+        cx.render(rsx!({ children }))
     }
 
     let mut dom = VirtualDom::new(App);
@@ -60,7 +60,7 @@ fn nested_passthru_creates_add() {
 
     #[component]
     fn ChildComp<'a>(cx: Scope, children: Element<'a>) -> Element {
-        cx.render(rsx! { children })
+        cx.render(rsx! { {children} })
     }
 
     let mut dom = VirtualDom::new(App);

+ 1 - 1
packages/core/tests/diff_component.rs

@@ -41,7 +41,7 @@ fn component_swap() {
         cx.render(rsx! {
             h1 {
                 "NavBar"
-                (0..3).map(|_| rsx!(nav_link {}))
+                {(0..3).map(|_| rsx!(nav_link {}))}
             }
         })
     }

+ 12 - 12
packages/core/tests/diff_keyed_list.rs

@@ -17,7 +17,7 @@ fn keyed_diffing_out_of_order() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     {
@@ -59,7 +59,7 @@ fn keyed_diffing_out_of_order_adds() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -85,7 +85,7 @@ fn keyed_diffing_out_of_order_adds_3() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -111,7 +111,7 @@ fn keyed_diffing_out_of_order_adds_4() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -137,7 +137,7 @@ fn keyed_diffing_out_of_order_adds_5() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -162,7 +162,7 @@ fn keyed_diffing_additions() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -187,7 +187,7 @@ fn keyed_diffing_additions_and_moves_on_ends() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -216,7 +216,7 @@ fn keyed_diffing_additions_and_moves_in_middle() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -250,7 +250,7 @@ fn controlled_keyed_diffing_out_of_order() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -284,7 +284,7 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -313,7 +313,7 @@ fn remove_list() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();
@@ -338,7 +338,7 @@ fn no_common_keys() {
             _ => unreachable!(),
         };
 
-        cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
+        cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) }))
     });
 
     _ = dom.rebuild();

+ 15 - 13
packages/core/tests/diff_unkeyed_list.rs

@@ -9,9 +9,9 @@ fn list_creates_one_by_one() {
 
         cx.render(rsx! {
             div {
-                (0..gen).map(|i| rsx! {
+                for i in 0..gen {
                     div { "{i}" }
-                })
+                }
             }
         })
     });
@@ -78,9 +78,9 @@ fn removes_one_by_one() {
 
         cx.render(rsx! {
             div {
-                (0..gen).map(|i| rsx! {
+                for i in 0..gen {
                     div { "{i}" }
-                })
+                }
             }
         })
     });
@@ -153,10 +153,10 @@ fn list_shrink_multiroot() {
     let mut dom = VirtualDom::new(|cx| {
         cx.render(rsx! {
             div {
-                (0..cx.generation()).map(|i| rsx! {
+                for i in 0..cx.generation() {
                     div { "{i}" }
                     div { "{i}" }
-                })
+                }
             }
         })
     });
@@ -214,10 +214,10 @@ fn removes_one_by_one_multiroot() {
 
         cx.render(rsx! {
             div {
-                (0..gen).map(|i| rsx! {
+                {(0..gen).map(|i| rsx! {
                     div { "{i}" }
                     div { "{i}" }
-                })
+                })}
             }
         })
     });
@@ -276,9 +276,9 @@ fn removes_one_by_one_multiroot() {
 fn two_equal_fragments_are_equal_static() {
     let mut dom = VirtualDom::new(|cx| {
         cx.render(rsx! {
-            (0..5).map(|_| rsx! {
+            for _ in 0..5 {
                 div { "hello" }
-            })
+            }
         })
     });
 
@@ -290,9 +290,9 @@ fn two_equal_fragments_are_equal_static() {
 fn two_equal_fragments_are_equal() {
     let mut dom = VirtualDom::new(|cx| {
         cx.render(rsx! {
-            (0..5).map(|i| rsx! {
+            for i in 0..5 {
                 div { "hello {i}" }
-            })
+            }
         })
     });
 
@@ -311,7 +311,9 @@ fn remove_many() {
         };
 
         cx.render(rsx! {
-            (0..num).map(|i| rsx! { div { "hello {i}" } })
+            for i in 0..num {
+                div { "hello {i}" }
+            }
         })
     });
 

+ 2 - 2
packages/core/tests/event_propagation.rs

@@ -58,11 +58,11 @@ fn app(cx: Scope) -> Element {
                 *CLICKS.lock().unwrap() += 1;
             },
 
-            vec![
+            {vec![
                 render! {
                     problematic_child {}
                 }
-            ].into_iter()
+            ].into_iter()}
         }
     }
 }

+ 1 - 1
packages/core/tests/lifecycle.rs

@@ -51,7 +51,7 @@ fn events_generate() {
                     "Click me!"
                 }
             }),
-            _ => cx.render(rsx!(())),
+            _ => None,
         }
     };
 

+ 2 - 2
packages/core/tests/miri_full_app.rs

@@ -39,9 +39,9 @@ fn App(cx: Scope) -> Element {
             }
             button { onclick: move |_| idx -= 1, "-" }
             ul {
-                (0..**idx).map(|i| rsx! {
+                {(0..**idx).map(|i| rsx! {
                     ChildExample { i: i, onhover: onhover }
-                })
+                })}
             }
         }
     })

+ 2 - 2
packages/core/tests/miri_stress.rs

@@ -15,7 +15,7 @@ fn test_memory_leak() {
         cx.spawn(async {});
 
         if val == 2 || val == 4 {
-            return cx.render(rsx!(()));
+            return None;
         }
 
         let name = cx.use_hook(|| String::from("numbers: "));
@@ -73,7 +73,7 @@ fn memo_works_properly() {
         let val = cx.generation();
 
         if val == 2 || val == 4 {
-            return cx.render(rsx!(()));
+            return None;
         }
 
         let name = cx.use_hook(|| String::from("asd"));

+ 1 - 1
packages/core/tests/task.rs

@@ -25,7 +25,7 @@ async fn it_works() {
             });
         });
 
-        cx.render(rsx!(()))
+        None
     }
 
     let mut dom = VirtualDom::new(app);

+ 1 - 1
packages/desktop/headless_tests/rendering.rs

@@ -89,7 +89,7 @@ fn check_html_renders(cx: Scope) -> Element {
                 h1 {
                     "text"
                 }
-                dyn_element
+                {dyn_element}
             }
         }
     }

+ 1 - 1
packages/dioxus-tui/examples/all_terminal_events.rs

@@ -71,7 +71,7 @@ fn app(cx: Scope) -> Element {
 
                 "Hover, click, type or scroll to see the info down below"
             }
-            div { width: "80%", height: "50%", flex_direction: "column", events_rendered }
+            div { width: "80%", height: "50%", flex_direction: "column", {events_rendered} }
         }
     })
 }

+ 23 - 33
packages/dioxus-tui/examples/buttons.rs

@@ -27,26 +27,25 @@ fn Button(cx: Scope<ButtonProps>) -> Element {
             height: "100%",
             background_color: "{color}",
             tabindex: "{cx.props.layer}",
-            onkeydown: |e| {
+            onkeydown: move |e| {
                 if let Code::Space = e.inner().code() {
                     toggle.modify(|f| !f);
                 }
             },
-            onclick: |_| {
+            onclick: move |_| {
                 toggle.modify(|f| !f);
             },
-            onmouseenter: |_|{
+            onmouseenter: move |_| {
                 hovered.set(true);
             },
-            onmouseleave: |_|{
+            onmouseleave: move |_|{
                 hovered.set(false);
             },
             justify_content: "center",
             align_items: "center",
             display: "flex",
             flex_direction: "column",
-
-            p{"tabindex: {cx.props.layer}"}
+            p{ "tabindex: {cx.props.layer}" }
         }
     })
 }
@@ -58,37 +57,28 @@ fn app(cx: Scope) -> Element {
             flex_direction: "column",
             width: "100%",
             height: "100%",
-
-            (1..8).map(|y|
-                rsx!{
-                    div{
-                        display: "flex",
-                        flex_direction: "row",
-                        width: "100%",
-                        height: "100%",
-                        (1..8).map(|x|{
-                            if (x + y) % 2 == 0{
-                                rsx!{
-                                    div{
-                                        width: "100%",
-                                        height: "100%",
-                                        background_color: "rgb(100, 100, 100)",
-                                    }
-                                }
+            for y in 1..8 {
+                div {
+                    display: "flex",
+                    flex_direction: "row",
+                    width: "100%",
+                    height: "100%",
+                    for x in 1..8 {
+                        if (x + y) % 2 == 0 {
+                            div {
+                                width: "100%",
+                                height: "100%",
+                                background_color: "rgb(100, 100, 100)",
                             }
-                            else{
-                                let layer = (x + y) % 3;
-                                rsx!{
-                                    Button{
-                                        color_offset: x * y,
-                                        layer: layer as u16,
-                                    }
-                                }
+                        } else {
+                            Button {
+                                color_offset: x * y,
+                                layer: ((x + y) % 3) as u16,
                             }
-                        })
+                        }
                     }
                 }
-            )
+            }
         }
     })
 }

+ 15 - 22
packages/dioxus-tui/examples/color_test.rs

@@ -14,32 +14,25 @@ fn app(cx: Scope) -> Element {
             width: "100%",
             height: "100%",
             flex_direction: "column",
-            (0..=steps).map(|x|
-                {
-                    let hue = x as f32*360.0/steps as f32;
-                    rsx! {
-                        div{
-                            width: "100%",
-                            height: "100%",
-                            flex_direction: "row",
-                            (0..=steps).map(|y|
-                                {
-                                    let alpha = y as f32*100.0/steps as f32;
-                                    rsx! {
-                                        div {
-                                            left: "{x}px",
-                                            top: "{y}px",
-                                            width: "10%",
-                                            height: "100%",
-                                            background_color: "hsl({hue}, 100%, 50%, {alpha}%)",
-                                        }
-                                    }
+            for x in 0..=steps {
+                div { width: "100%", height: "100%", flex_direction: "row",
+                    for y in 0..=steps {
+                        {
+                            let hue = x as f32*360.0/steps as f32;
+                            let alpha = y as f32*100.0/steps as f32;
+                            rsx! {
+                                div {
+                                    left: "{x}px",
+                                    top: "{y}px",
+                                    width: "10%",
+                                    height: "100%",
+                                    background_color: "hsl({hue}, 100%, 50%, {alpha}%)",
                                 }
-                            )
+                            }
                         }
                     }
                 }
-            )
+            }
         }
     })
 }

+ 2 - 2
packages/dioxus-tui/examples/list.rs

@@ -19,9 +19,9 @@ fn app(cx: Scope) -> Element {
             ul {
                 flex_direction: "column",
                 padding_left: "3px",
-                (0..10).map(|i| rsx!(
+                for i in 0..10 {
                     "> hello {i}"
-                ))
+                }
             }
         }
     })

+ 18 - 22
packages/dioxus-tui/examples/many_small_edit_stress.rs

@@ -71,32 +71,28 @@ fn Grid(cx: Scope<GridProps>) -> Element {
             width: "100%",
             height: "100%",
             flex_direction: "column",
-            (0..size).map(|x|
-                    {
-                    rsx! {
-                        div{
-                            width: "100%",
-                            height: "100%",
-                            flex_direction: "row",
-                            (0..size).map(|y|
-                                {
-                                    let alpha = y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32;
-                                    let key = format!("{}-{}", x, y);
-                                    rsx! {
-                                        Box {
-                                            x: x,
-                                            y: y,
-                                            alpha: 100.0,
-                                            hue: alpha,
-                                            key: "{key}",
-                                        }
-                                    }
+            for x in 0..size {
+                div{
+                    width: "100%",
+                    height: "100%",
+                    flex_direction: "row",
+                    for y in 0..size {
+                        {
+                            let alpha = y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32;
+                            let key = format!("{}-{}", x, y);
+                            rsx! {
+                                Box {
+                                    x: x,
+                                    y: y,
+                                    alpha: 100.0,
+                                    hue: alpha,
+                                    key: "{key}",
                                 }
-                            )
+                            }
                         }
                     }
                 }
-            )
+            }
         }
     }
 }

+ 6 - 4
packages/dioxus/examples/stress.rs

@@ -17,10 +17,12 @@ fn app(cx: Scope) -> Element {
     cx.render(rsx! (
         table {
             tbody {
-                (0..10_000_usize).map(|f| {
-                    let label = Label::new(&mut rng);
-                    rsx!( table_row { row_id: f, label: label } )
-                })
+                for f in 0..10_000_usize {
+                    table_row {
+                        row_id: f,
+                        label: Label::new(&mut rng)
+                    }
+                }
             }
         }
     ))

+ 11 - 26
packages/native-core/src/utils/persistant_iterator.rs

@@ -306,15 +306,10 @@ fn persist_removes() {
             _ => unreachable!(),
         };
         render!(
-            div{
-                (0..children).map(|i|{
-                    rsx!{
-                        p{
-                            key: "{i}",
-                            "{i}"
-                        }
-                    }
-                })
+            div {
+                for i in 0..children {
+                    p { key: "{i}", "{i}" }
+                }
             }
         )
     }
@@ -387,15 +382,10 @@ fn persist_instertions_before() {
             _ => unreachable!(),
         };
         render!(
-            div{
-                (0..children).map(|i|{
-                    rsx!{
-                        p{
-                            key: "{i}",
-                            "{i}"
-                        }
-                    }
-                })
+            div {
+                for i in 0..children {
+                    p { key: "{i}", "{i}" }
+                }
             }
         )
     }
@@ -446,14 +436,9 @@ fn persist_instertions_after() {
         };
         render!(
             div{
-                (0..children).map(|i|{
-                    rsx!{
-                        p{
-                            key: "{i}",
-                            "{i}"
-                        }
-                    }
-                })
+                for i in 0..children {
+                    p { key: "{i}", "{i}" }
+                }
             }
         )
     }

+ 1 - 1
packages/router/examples/simple_routes.rs

@@ -103,7 +103,7 @@ fn Route1(cx: Scope, user_id: usize, dynamic: usize, query: String, extra: Strin
 fn Route2(cx: Scope, user_id: usize) -> Element {
     render! {
         pre { "Route2{{\n\tuser_id:{user_id}\n}}" }
-        (0..*user_id).map(|i| rsx!{ p { "{i}" } }),
+        {(0..*user_id).map(|i| rsx!{ p { "{i}" } })},
         p { "Footer" }
         Link {
             to: Route::Route3 {

+ 2 - 2
packages/router/src/components/history_buttons.rs

@@ -77,7 +77,7 @@ pub fn GoBackButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element {
             disabled: "{disabled}",
             prevent_default: "onclick",
             onclick: move |_| router.go_back(),
-            children
+            {children}
         }
     }
 }
@@ -149,7 +149,7 @@ pub fn GoForwardButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element {
             disabled: "{disabled}",
             prevent_default: "onclick",
             onclick: move |_| router.go_forward(),
-            children
+            {children}
         }
     }
 }

+ 2 - 2
packages/router/src/components/link.rs

@@ -167,7 +167,7 @@ impl Debug for LinkProps<'_> {
 ///                 new_tab: true,
 ///                 rel: "link_rel",
 ///                 to: Route::Index {},
-///    
+///
 ///                 "A fully configured link"
 ///             }
 ///         }
@@ -250,7 +250,7 @@ pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
             id: "{id}",
             rel: "{rel}",
             target: "{tag_target}",
-            children
+            {children}
         }
     }
 }

+ 117 - 68
packages/rsx/src/node.rs

@@ -6,7 +6,8 @@ use syn::{
     braced,
     parse::{Parse, ParseStream},
     spanned::Spanned,
-    token, Expr, ExprIf, LitStr, Pat, Result,
+    token::{self, Brace},
+    Expr, ExprIf, LitStr, Pat, Result,
 };
 
 /*
@@ -15,14 +16,14 @@ Parse
 -> Component {}
 -> component()
 -> "text {with_args}"
--> (0..10).map(|f| rsx!("asd")),  // <--- notice the comma - must be a complete expr
+-> {(0..10).map(|f| rsx!("asd"))}  // <--- notice the curly braces
 */
 #[derive(PartialEq, Eq, Clone, Debug, Hash)]
 pub enum BodyNode {
     Element(Element),
     Component(Component),
     ForLoop(ForLoop),
-    IfChain(ExprIf),
+    IfChain(IfChain),
     Text(IfmtInput),
     RawExpr(Expr),
 }
@@ -112,7 +113,27 @@ impl Parse for BodyNode {
             return Ok(BodyNode::IfChain(stream.parse()?));
         }
 
-        Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
+        // Match statements are special but have no special arm syntax
+        // we could allow arm syntax if we wanted
+        //
+        // ```
+        // match {
+        //  val => div {}
+        //  other_val => div {}
+        // }
+        // ```
+        if stream.peek(Token![match]) {
+            return Ok(BodyNode::RawExpr(stream.parse::<Expr>()?));
+        }
+
+        if stream.peek(token::Brace) {
+            return Ok(BodyNode::RawExpr(stream.parse::<Expr>()?));
+        }
+
+        Err(syn::Error::new(
+            stream.span(),
+            "Expected a valid body node.\nExpressions must be wrapped in curly braces.",
+        ))
     }
 }
 
@@ -151,71 +172,52 @@ impl ToTokens for BodyNode {
                 })
             }
             BodyNode::IfChain(chain) => {
-                if is_if_chain_terminated(chain) {
-                    tokens.append_all(quote! {
-                        {
-                            let ___nodes = (#chain).into_dyn_node(__cx);
-                            ___nodes
-                        }
-                    });
-                } else {
-                    let ExprIf {
+                let mut body = TokenStream2::new();
+                let mut terminated = false;
+
+                let mut elif = Some(chain);
+
+                while let Some(chain) = elif {
+                    let IfChain {
+                        if_token,
                         cond,
                         then_branch,
+                        else_if_branch,
                         else_branch,
-                        ..
                     } = chain;
 
-                    let mut body = TokenStream2::new();
-
-                    body.append_all(quote! {
-                        if #cond {
-                            Some(#then_branch)
-                        }
-                    });
-
-                    let mut elif = else_branch;
-
-                    while let Some((_, ref branch)) = elif {
-                        match branch.as_ref() {
-                            Expr::If(ref eelif) => {
-                                let ExprIf {
-                                    cond,
-                                    then_branch,
-                                    else_branch,
-                                    ..
-                                } = eelif;
-
-                                body.append_all(quote! {
-                                    else if #cond {
-                                        Some(#then_branch)
-                                    }
-                                });
-
-                                elif = else_branch;
-                            }
-                            _ => {
-                                body.append_all(quote! {
-                                    else {
-                                        #branch
-                                    }
-                                });
-                                break;
-                            }
-                        }
+                    let mut renderer: TemplateRenderer = TemplateRenderer {
+                        roots: then_branch,
+                        location: None,
+                    };
+
+                    body.append_all(quote! { #if_token #cond { Some({#renderer}) } });
+
+                    if let Some(next) = else_if_branch {
+                        body.append_all(quote! { else });
+                        elif = Some(next);
+                    } else if let Some(else_branch) = else_branch {
+                        renderer.roots = else_branch;
+                        body.append_all(quote! { else { Some({#renderer}) } });
+                        terminated = true;
+                        break;
+                    } else {
+                        elif = None;
                     }
+                }
 
+                if !terminated {
                     body.append_all(quote! {
                         else { None }
                     });
-
-                    tokens.append_all(quote! {
-                        {
-                            let ___nodes = (#body).into_dyn_node(__cx);
-                            ___nodes
-                        }
-                    });
                 }
+
+                tokens.append_all(quote! {
+                    {
+                        let ___nodes = (#body).into_dyn_node(__cx);
+                        ___nodes
+                    }
+                });
             }
         }
     }
@@ -240,26 +242,73 @@ impl Parse for ForLoop {
         let in_token: Token![in] = input.parse()?;
         let expr: Expr = input.call(Expr::parse_without_eager_brace)?;
 
-        let content;
-        let brace_token = braced!(content in input);
-
-        let mut children = vec![];
-
-        while !content.is_empty() {
-            children.push(content.parse()?);
-        }
+        let (brace_token, body) = parse_buffer_as_braced_children(input)?;
 
         Ok(Self {
             for_token,
             pat,
             in_token,
-            body: children,
-            expr: Box::new(expr),
+            body,
             brace_token,
+            expr: Box::new(expr),
+        })
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Debug, Hash)]
+pub struct IfChain {
+    pub if_token: Token![if],
+    pub cond: Box<Expr>,
+    pub then_branch: Vec<BodyNode>,
+    pub else_if_branch: Option<Box<IfChain>>,
+    pub else_branch: Option<Vec<BodyNode>>,
+}
+
+impl Parse for IfChain {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let if_token: Token![if] = input.parse()?;
+
+        // stolen from ExprIf
+        let cond = Box::new(input.call(Expr::parse_without_eager_brace)?);
+
+        let (_, then_branch) = parse_buffer_as_braced_children(input)?;
+
+        let mut else_branch = None;
+        let mut else_if_branch = None;
+
+        // if the next token is `else`, set the else branch as the next if chain
+        if input.peek(Token![else]) {
+            input.parse::<Token![else]>()?;
+            if input.peek(Token![if]) {
+                else_if_branch = Some(Box::new(input.parse::<IfChain>()?));
+            } else {
+                let (_, else_branch_nodes) = parse_buffer_as_braced_children(input)?;
+                else_branch = Some(else_branch_nodes);
+            }
+        }
+
+        Ok(Self {
+            cond,
+            if_token,
+            then_branch,
+            else_if_branch,
+            else_branch,
         })
     }
 }
 
+fn parse_buffer_as_braced_children(
+    input: &syn::parse::ParseBuffer<'_>,
+) -> Result<(Brace, Vec<BodyNode>)> {
+    let content;
+    let brace_token = braced!(content in input);
+    let mut then_branch = vec![];
+    while !content.is_empty() {
+        then_branch.push(content.parse()?);
+    }
+    Ok((brace_token, then_branch))
+}
+
 pub(crate) fn is_if_chain_terminated(chain: &ExprIf) -> bool {
     let mut current = chain;
     loop {

+ 3 - 1
packages/ssr/src/renderer.rs

@@ -242,7 +242,9 @@ fn to_string_works() {
                 div {}
                 div { "nest 2" }
                 "{dyn2}"
-                (0..5).map(|i| rsx! { div { "finalize {i}" } })
+                for i in (0..5) {
+                    div { "finalize {i}" }
+                }
             }
         }
     }

+ 3 - 7
packages/ssr/tests/hydration.rs

@@ -70,7 +70,7 @@ fn text_nodes() {
     fn app(cx: Scope) -> Element {
         let dynamic_text = "hello";
         render! {
-            div { dynamic_text }
+            div { {dynamic_text} }
         }
     }
 
@@ -124,7 +124,7 @@ fn components_hydrate() {
     fn Child2(cx: Scope) -> Element {
         let dyn_text = "hello";
         render! {
-            div { dyn_text }
+            div { {dyn_text} }
         }
     }
 
@@ -159,11 +159,7 @@ fn components_hydrate() {
     fn Child4(cx: Scope) -> Element {
         render! {
             for _ in 0..2 {
-                render! {
-                    render! {
-                        "{1}"
-                    }
-                }
+                {render! { "{1}" }}
             }
         }
     }

+ 7 - 5
packages/ssr/tests/simple.rs

@@ -23,9 +23,9 @@ fn lists() {
     assert_eq!(
         dioxus_ssr::render_lazy(rsx! {
             ul {
-                (0..5).map(|i| rsx! {
+                for i in 0..5 {
                     li { "item {i}" }
-                })
+                }
             }
         }),
         "<ul><li>item 0</li><li>item 1</li><li>item 2</li><li>item 3</li><li>item 4</li></ul>"
@@ -53,9 +53,9 @@ fn components() {
     assert_eq!(
         dioxus_ssr::render_lazy(rsx! {
             div {
-                (0..5).map(|name| rsx! {
+                for name in 0..5 {
                     MyComponent { name: name }
-                })
+                }
             }
         }),
         "<div><div>component 0</div><div>component 1</div><div>component 2</div><div>component 3</div><div>component 4</div></div>"
@@ -67,7 +67,9 @@ fn fragments() {
     assert_eq!(
         dioxus_ssr::render_lazy(rsx! {
             div {
-                (0..5).map(|_| rsx! (()))
+                for _ in 0..5 {
+                    {()}
+                }
             }
         }),
         "<div></div>"

+ 2 - 2
packages/web/examples/hydrate.rs

@@ -12,11 +12,11 @@ fn app(cx: Scope) -> Element {
             "asd"
             Bapp {}
         }
-        (0..10).map(|f| rsx!{
+        {(0..10).map(|f| rsx!{
             div {
                 "thing {f}"
             }
-        })
+        })}
     })
 }
 

+ 5 - 2
packages/web/examples/timeout_count.rs

@@ -24,8 +24,11 @@ fn app(cx: Scope) -> Element {
                 start();
                 *count.write() += 1;
             },
-            // format is needed as {count} does not seemed to work in `if` within content
-            if **started { format!("Current score: {}", count.write()) } else { "Start".to_string() }
+            if **started {
+                "Current score: {count.read()}"
+            } else {
+                "Start"
+            }
         }
     })
 }

+ 1 - 1
packages/web/tests/hydrate.rs

@@ -43,7 +43,7 @@ fn rehydrates() {
                     },
                     "listener test"
                 }
-                false.then(|| rsx!{ "hello" })
+                {false.then(|| rsx!{ "hello" })}
             }
         })
     }