Components are the building blocks of Freya applications. They let you split your UI into independent, reusable pieces that can be composed together to create complex interfaces.
In Freya, a component is any data type (usually a struct) that implements the Component trait. Components are declarative - you don’t instantiate them manually, you simply declare them and Freya takes care of rendering them.
All component structs must implement PartialEq. This is how Freya determines when a component needs to re-render.
A “render” is when the component’s render method runs:
use freya::prelude::*;#[derive(PartialEq)]struct Counter;impl Component for Counter { // This function runs every time the component "renders" fn render(&self) -> impl IntoElement { let mut count = use_state(|| 0); rect() .on_mouse_up(move |_| { // Mutating state causes a re-render *count.write() += 1; }) .child(format!("Count: {}", count.read())) }}
A component render doesn’t necessarily mean the window redraws. Freya intelligently diffs the UI tree and only repaints when visual changes occur.
Each component should do one thing well. Break down complex UIs into smaller components:
// ❌ Bad - too much in one componentfn app() -> impl IntoElement { rect() .child(/* header */) .child(/* sidebar */) .child(/* main content */) .child(/* footer */)}// ✅ Good - separated concernsfn app() -> impl IntoElement { rect() .child(Header {}) .child(Sidebar {}) .child(MainContent {}) .child(Footer {})}
Avoid expensive computations in render
The render method can run frequently. Move expensive operations to effects or memoize them:
// ❌ Bad - computes on every renderfn render(&self) -> impl IntoElement { let result = expensive_computation(); rect().child(result)}// ✅ Good - memoized computationfn render(&self) -> impl IntoElement { let result = use_memo(|| expensive_computation()); rect().child(result.read())}
Use Cow for string props
This avoids unnecessary allocations:
use std::borrow::Cow;#[derive(PartialEq)]struct Label(Cow<'static, str>);