Props allow you to pass data from parent components to child components. They make components reusable and help you build composable UIs.
The Props Trait
Props in Dioxus must implement the Properties trait:
pub trait Properties : Clone + Sized + ' static {
type Builder ;
fn builder () -> Self :: Builder ;
fn memoize ( & mut self , other : & Self ) -> bool ;
}
Key requirements:
Clone : Props must be cloneable
'static : Props must have a static lifetime
PartialEq : Required for memoization (usually via derive)
Using the #[component] Macro
The recommended way to define props is using the #[component] macro:
use dioxus :: prelude ::* ;
#[component]
fn Greeting ( name : String , age : i32 ) -> Element {
rsx! {
p { "Hello, {name}! You are {age} years old." }
}
}
// Usage
rsx! {
Greeting { name : "Alice" , age : 30 }
}
This automatically generates a GreetingProps struct with the Properties trait implemented.
Manual Props Definition
You can also manually define props using #[derive(Props)]:
use dioxus :: prelude ::* ;
#[derive( Props , PartialEq , Clone )]
struct ButtonProps {
text : String ,
color : String ,
}
fn Button ( props : ButtonProps ) -> Element {
rsx! {
button {
style : "color: {props.color}" ,
"{props.text}"
}
}
}
The #[component] macro is preferred because it provides better error messages and additional compile-time checks.
Optional Props
Using Option<T>
Fields with type Option<T> are automatically optional:
#[component]
fn Avatar (
url : String ,
size : Option < u32 >,
) -> Element {
let size = size . unwrap_or ( 64 );
rsx! {
img {
src : "{url}" ,
width : "{size}px" ,
height : "{size}px" ,
}
}
}
// Usage - size is optional
rsx! {
Avatar { url : "avatar.png" }
Avatar { url : "avatar.png" , size : 128 }
}
Using #[props(default)]
Provide default values for optional props:
#[component]
fn Button (
text : String ,
#[ props ( default )]
disabled : bool ,
#[ props ( default = "primary" . to_string ())]
variant : String ,
) -> Element {
rsx! {
button {
class : "btn btn-{variant}" ,
disabled : disabled ,
"{text}"
}
}
}
// Usage
rsx! {
Button { text : "Click me" }
Button { text : "Disabled" , disabled : true }
Button { text : "Secondary" , variant : "secondary" }
}
Making Option<T> Required
Use #[props(!optional)] to require an Option<T> field:
#[component]
fn MaybeShow (
#[ props ( ! optional )]
content : Option < String >,
) -> Element {
rsx! {
if let Some ( text ) = content {
p { "{text}" }
}
}
}
// Must explicitly pass Some or None
rsx! {
MaybeShow { content : Some ( "Hello" . to_string ()) }
MaybeShow { content : None }
}
Type Conversions
Using #[props(into)]
Automatically convert types using the Into trait:
#[component]
fn Label (
#[ props ( into )]
text : String ,
#[ props ( into )]
count : i64 ,
) -> Element {
rsx! {
span { "{text}: {count}" }
}
}
// Can pass &str instead of String, i32 instead of i64
rsx! {
Label { text : "Items" , count : 42 i32 }
}
String props accept formatted strings:
#[component]
fn Message ( text : String ) -> Element {
rsx! { p { "{text}" } }
}
let name = "World" ;
rsx! {
Message { text : "Hello, {name}!" }
}
Children Props
Accept child elements with the children: Element parameter:
#[component]
fn Card (
title : String ,
children : Element ,
) -> Element {
rsx! {
div { class : "card" ,
h2 { "{title}" }
div { class : "card-body" ,
{ children }
}
}
}
}
// Usage
rsx! {
Card { title : "My Card" ,
p { "This is the card content" }
button { "Action" }
}
}
Reactive Props
Using ReadSignal<T>
Make props reactive by wrapping them in ReadSignal:
#[component]
fn Counter ( count : ReadSignal < i32 >) -> Element {
// The memo will automatically rerun when count changes
let doubled = use_memo ( move || count () * 2 );
rsx! {
div {
"Count: {count}"
"Doubled: {doubled}"
}
}
}
// Dioxus automatically converts values to ReadSignal
let mut count = use_signal ( || 0 );
rsx! {
Counter { count : count () }
// Or pass the signal directly
Counter { count }
}
Use ReadSignal props when you need hooks like use_memo or use_effect to react to prop changes.
Non-Reactive Props
Regular props trigger component re-render when changed, but don’t work with reactive hooks:
#[component]
fn Display ( value : i32 ) -> Element {
// This memo will NOT update when value changes
// because value is not reactive
let doubled = use_memo ( move || value * 2 );
rsx! {
"Value: {value}"
"Doubled: {doubled}"
}
}
To fix this, either:
Use ReadSignal<T> (recommended)
Use use_reactive! macro to explicitly track dependencies
Event Handler Props
Pass callbacks using EventHandler:
#[component]
fn ClickableCard (
title : String ,
on_click : EventHandler <()>,
) -> Element {
rsx! {
div {
class : "card" ,
onclick : move | _ | on_click . call (()),
"{title}"
}
}
}
// Usage
let mut count = use_signal ( || 0 );
rsx! {
ClickableCard {
title : "Click me!" ,
on_click : move | _ | count += 1 ,
}
}
Extending Element Attributes
Accept arbitrary HTML attributes using #[props(extends)]:
#[component]
fn CustomDiv (
#[ props ( extends = GlobalAttributes )]
attributes : Vec < Attribute >,
children : Element ,
) -> Element {
rsx! {
div {
.. attributes ,
{ children }
}
}
}
// Usage - can use any global attribute
rsx! {
CustomDiv {
class : "container" ,
id : "main" ,
style : "padding: 1rem" ,
"Content"
}
}
Extending Specific Elements
#[component]
fn CustomButton (
#[ props ( extends = GlobalAttributes , extends = button )]
attributes : Vec < Attribute >,
text : String ,
) -> Element {
rsx! {
button { .. attributes , "{text}" }
}
}
rsx! {
CustomButton {
text : "Submit" ,
disabled : true ,
class : "btn-primary" ,
}
}
Prop Spreading
Spread props from a struct:
#[component]
fn StyledButton (
text : String ,
color : String ,
) -> Element {
rsx! {
button { style : "color: {color}" , "{text}" }
}
}
let button_props = StyledButtonProps {
text : "Click me" . to_string (),
color : "blue" . to_string (),
};
rsx! {
StyledButton { .. button_props }
// Can override individual props
StyledButton {
text : "Different text" ,
.. button_props
}
}
Best Practices
Use #[component] Macro Prefer #[component] over manual #[derive(Props)] for better error messages and type checking.
Keep Props Simple Props should be simple, cloneable types. For complex state, use context or global signals.
Implement PartialEq Always derive PartialEq to enable memoization and prevent unnecessary re-renders.
Use ReadSignal for Reactivity Wrap props in ReadSignal<T> when you need reactive hooks to respond to changes.
See Also
Components - Learn about component basics
Signals - Understand reactive state with signals
State - Manage component and application state