Properties
note
Properties are often shortened as “Props”.
Properties are essentially component arguments that Yew can keep watch on.
A type has to implement the Properties
trait before it can be used as the properties of a component.
Reactivity
Yew checks if props have changed when reconciling the vdom during rerendering, to know if nested components needs to be rerendered. This way Yew can be considered a very reactive framework as changes from the parent will always be propagated downwards and the view will never be out of sync from the data coming from props/state.
tip
If you have not yet completed the tutorial, try it out and test this reactivity yourself!
Derive macro
Yew provides a derive macro to easily implement the Properties
trait on structs.
Types for which you derive Properties
must also implement PartialEq
so Yew can do data comparison.
use yew::Properties;
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
Use in function components
The attribute #[function_component]
allows to optionally receive Props in the function arguments. To supply them, they are assigned via attributes in the html!
macro.
- With Props
- No Props
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! { <>{"Am I loading? - "}{props.is_loading.clone()}</> }
}
// Then supply the prop
#[function_component]
fn App() -> Html {
html! {<HelloWorld is_loading={true} />}
}
use yew::{function_component, html, Html};
#[function_component]
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// No props to supply
#[function_component]
fn App() -> Html {
html! {<HelloWorld />}
}
Derive macro field attributes
When deriving Properties
all fields are required by default. The following attributes allow you to give your props default values which will be used when parent has not set them.
tip
Attributes aren’t visible in Rustdoc generated documentation. The doc strings of your properties should mention whether a prop is optional and if it has a special default value.
Initialize the prop value with the default value of the field’s type using the Default
trait.
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or_default]
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
if props.is_loading.clone() {
html! { "Loading" }
} else {
html! { "Hello world" }
}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld is_loading={true} />}
}
Use value
to initialize the prop value. value
can be any expression that returns the field’s type. For example, to default a boolean prop to true
, use the attribute #[prop_or(true)]
. The expression is evaluated when the properties are constructed and no explicit value has been given.
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or("Bob".to_string())]
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
}
Call function
to initialize the prop value. function
should have the signature FnMut() -> T
where T
is the field type. The function is called when no explicit value has been given for that attribute.
use yew::{function_component, html, Html, Properties};
fn create_default_name() -> String {
"Bob".to_string()
}
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or_else(create_default_name)]
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
}
Memory/speed overhead of using Properties
Internally properties are reference counted. This means that only a shared pointer is passed down the component tree for props. It saves us from the cost of having to clone the entire props, which might be expensive.
tip
Make use of AttrValue
which is our custom type for attribute values instead of defining them as String or another similar type.
Props macro
The yew::props!
macro allows you to build properties the same way the html!
macro does it.
The macro uses the same syntax as a struct expression except that you can’t use attributes or a base expression (Foo { ..base }
). The type path can either point to the props directly (path::to::Props
) or the associated properties of a component (MyComp::Properties
).
use yew::{function_component, html, Html, Properties, props, virtual_dom::AttrValue};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or(AttrValue::from("Bob"))]
pub name: AttrValue,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
#[function_component]
fn App() -> Html {
let pre_made_props = props! {
Props {} // Notice we did not need to specify name prop
};
html! {<HelloWorld ..pre_made_props />}
}
Evaluation Order
Props are evaluated in the order they’re specified, as shown by the following example:
#[derive(yew::Properties, PartialEq)]
struct Props { first: usize, second: usize, last: usize }
fn main() {
let mut g = 1..=3;
let props = yew::props!(Props { first: g.next().unwrap(), second: g.next().unwrap(), last: g.next().unwrap() });
assert_eq!(props.first, 1);
assert_eq!(props.second, 2);
assert_eq!(props.last, 3);
}
Anti Patterns
While almost any Rust type can be passed as properties, there are some anti-patterns that should be avoided. These include, but are not limited to:
- Using
String
type instead ofAttrValue
.
Why is this bad?String
can be expensive to clone. Cloning is often needed when the prop value is used with hooks and callbacks.AttrValue
is either a reference-counted string (Rc<str>
) or a&'static str
, thus very cheap to clone.
Note:AttrValue
internally isIString
from implicit-clone See that crate to learn more. - Using interior mutability.
Why is this bad? Interior mutability (such as withRefCell
,Mutex
, etc.) should generally be avoided. It can cause problems with re-renders (Yew doesn’t know when state has changed) so you may have to manually force a render. Like all things, it has its place. Use it with caution. - You tell us. Did you run into an edge-case you wish you knew about earlier? Feel free to create an issue or PR a fix to this documentation.