rsx syntax

Frender provides the rsx macro to create elements with syntax similar to jsx.

Create elements with props

rsx prop key should be an valid ident. Any literal string or expression wrapped in braces can be used as rsx prop value.


#![allow(unused)]
fn main() {
rsx!(
    <MyComponent
        prop1="my-literal-string"
        prop2={my_expr}
        prop3
    />
)
}

The above code requires MyComponent to implements StaticComponent, and will be transformed into something like the following code, which makes props strongly typed.


#![allow(unused)]
fn main() {
MyComponent::create_element(
    MyComponent::Props::init_builder()
        .prop1("my-literal-string".into_prop_value())
        .prop2(my_expr.into_prop_value())
        .prop3(true.into_prop_value())
        .build()
)
}

Prop value auto conversion

Notice the my_expr.into_prop_value() in the above example. IntoPropValue::into_prop_value trait is introduced for auto converting prop values, so that rsx props can be less verbose.

For example, assuming the index prop of MyComponent accepts Option<i32>, you can create the element with any of the following codes.


#![allow(unused)]
fn main() {
rsx! ( <MyComponent index={Some(1)} /> )
rsx! ( <MyComponent index={1} /> )
}

IntoPropValue<Option<T>> is implemented for any type T. Thus, 1 can be converted to Some(1) automatically when used rsx prop value.

To disable the auto conversion, you can use := to set prop prop:={value}. For example:


#![allow(unused)]
fn main() {
rsx!(
    <MyComponent prop1:="value" prop2="value" />
)
}

will be transformed into


#![allow(unused)]
fn main() {
MyComponent::create_element(
    MyComponent::Props::init_builder()
        .prop1("value")
        .prop2("value".into_prop_value())
        .build()
)
}

Create elements with children

Any literals, rsx elements or rust expressions wrapped in braces can be a valid child in rsx elements.

Elements with multiple children will accept a tuple of these values into children prop. For example:


#![allow(unused)]
fn main() {
rsx!(
    <MyComponent>
        "my-literal-string"
        1
        {my_expr}
        <div id="my-div" />
    </MyComponent>
)
}

is equivalent to the following code:


#![allow(unused)]
fn main() {
rsx!(
    <MyComponent
        children={
            (
                "my-literal-string",
                1,
                {my_expr},
                rsx!( <div id="my-div" /> ),
            )
        }
    />
)
}

Elements with exactly one child will accept a single value into children prop, not wrapped in a tuple. For example:


#![allow(unused)]
fn main() {
rsx!(
    <MyComponent>
        <div id="my-div" />
    </MyComponent>
)
}

will be transformed to the following code:


#![allow(unused)]
fn main() {
rsx!(
    <MyComponent
        children={rsx!( <div id="my-div" /> )}
    />
)
}

Create intrinsic elements

Any component name starting with lower case letter [a-z] will be interpreted as an intrinsic component. For example, rsx!( <div id="my-div" /> ) will be resolved to:


#![allow(unused)]
fn main() {
use frender::prelude::*;
use self::intrinsic_components::div::prelude::*;

rsx! (
  <self::intrinsic_components::div::prelude::Component id="my-div" />
)
}

Notice the self::intrinsic_components. This means rsx searches for intrinsic_components module in the current module instead of sub module of frender. This would allow you to import a custom intrinsic_components in a module level.

Keyed element

key prop is preserved as a keyword, like React.js. When key prop is specified, the element will be wrapped with Keyed<TElement>, indicating the element is created with a key. For example:


#![allow(unused)]
fn main() {
use react::{Element, Keyed};

let element: Element = rsx!( <div /> );
let keyed_element: Keyed<Element> = rsx!( <div key="my-div" /> );
}

Fragment element


#![allow(unused)]
fn main() {
rsx!(
    <>
        "create"
        <i>"react"</i>
        "fragment"
        {"to wrap multiple children"}
    </>
)
}

The above rsx will be transformed into:


#![allow(unused)]
fn main() {
rsx!(
    <self::rsx_runtime::Fragment>
        "create"
        <i>"react"</i>
        "fragment"
        {"to wrap multiple children"}
    </self::rsx_runtime::Fragment>
)
}

To create fragment element with key, you can use # as the component name.


#![allow(unused)]
fn main() {
rsx!(
    <# key="my-key">1 2 3</#>
)
}

Note that Fragment component outputs a custom element type FragmentElement. It implements Into<react::Element>.

Enclose any element with </_>

You can enclose any element with </_>, which is very useful when the component type has a long module path or complex generics.


#![allow(unused)]
fn main() {
rsx!(
    <>
        <path::to::Component></_>
        // the above is equivalent to:
        <path::to::Component></path::to::Component>

        <MyComponent<i32>>1</_>
        // the above is equivalent to:
        <MyComponent<i32>>1</MyComponent<i32>>
    </>
)
}