Write a component

With the #[component] macro, writing a component in frender is as simple as writing a rust fn. The macro will turn the fn item to a struct, and implement UseRenderStatic and ComponentStatic for it. The fn body will be the body of UseRenderStatic::use_render method. The return type of the fn will be UseRenderStatic::RenderOutput. The optional first arg of the fn will be the props type of this component, which is UseRenderStatic::RenderArg and [ComponentStatic::Props]. If there is no args in the fn, the props type will be react::NoProps.

The return type can be omitted. If omitted, the default return type is react::Element.

Component with no props


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

#[component]
fn MyComponent() {
  //            ^
  //            the return type defaults to react::Element
  rsx!( <div /> )
}

fn check_something() -> bool { true }

// Or you can specify the return type explicitly
#[component]
fn MyAnotherComponent() -> Option<react::Element> {
  if check_something() {
    Some(rsx!( <MyComponent /> ))
  } else {
    None
  }
}
}

Component with props

First, define props with def_props macro.


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

def_props! {
  pub struct MyProps {
    // Required prop
    name: String,

    // Optional prop which defaults to `Default::default()`
    // The following property `age` is optional, and defaults to `None`
    age?: Option<u8>,

    // The following property `tags` is optional, and defaults to `Vec::default()`
    tags?: Vec<String>,

    // If the prop type is not specified,
    // then frender will infer the type by prop name.
    // For example, `class_name` default has type `Option<String>`
    // The following property `class_name` is optional, has type Option<String>
    class_name?,

    // The following property `id` is required, has type Option<String>
    id,

    // Prop can also have type generics.
    // For example, the following is
    // the default definition for prop `children`,
    // which means it accepts any `Option<TNode>` where TNode implements react::Node,
    // and then map the value into `Option<react::Children>` and store it into MyProps.
    children<TNode: react::Node>(value: Option<TNode>) -> Option<react::Children> {
      value.and_then(react::Node::into_children)
    },
  }
}
}

Then write the component with the above props:


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

#[component]
pub fn MyComponent(props: &MyProps) {
    rsx!(
        <div>{&props.children}</div>
    )
}
}

Due to the generics, in some very rare cases, you may meet errors like type annotations needed cannot infer type for type parameter. You can solve it by specifying the type with the turbofish syntax ::<>. For example:


#![allow(unused)]
fn main() {
rsx! (
  // ERROR: type annotations needed
  <a children={None} />
)
rsx! (
  // it works!
  <a children={None::<()>} />
)
}