Part 7 of 10 21 Jan 2026 By Raj Patil 30 min read

Part 7: Advanced Patterns in Freya - Custom Components & Popups

Part 7 of the Freya Rust GUI series. Build sophisticated apps using advanced patterns like custom reusable components, global Context, and Popups.

Advanced #rust #freya #components #architecture #context #modal #advanced-patterns
Building Native GUIs with Rust & Freya 7 / 10

Advanced Patterns

Now that you understand the basics, I’ll explore advanced patterns for building sophisticated applications.

Custom Components

Create reusable components by defining functions:

struct CardProps {
    title: String,
    content: String,
}

fn Card(props: CardProps) -> impl IntoElement {
    rect()
        .background(Color::from_rgb(30, 30, 30))
        .corner_radius(12.0)
        .padding(20.0)
        .shadow(Shadow::new(0.0, 4.0, 12.0, 0.0, Color::from_rgba(0, 0, 0, 0.3)))
        .child(
            label()
                .font_size(20.0)
                .font_weight(FontWeight::BOLD)
                .margin(Margin::new(0.0, 0.0, 10.0, 0.0)) // Bottom margin if available, or wrapper
                .child(props.title)
        )
        .child(label().text(props.content))
}

fn app() -> impl IntoElement {
    Card(CardProps {
        title: "My Card".to_string(),
        content: "Card content goes here".to_string(),
    })
}

Context for Global State

Share state across components:

#[derive(Clone, Copy)]
struct ThemeContext {
    is_dark: bool,
}

fn app() -> impl IntoElement {
    use_context_provider(|| ThemeContext { is_dark: true });
    
    // Components inside here can consume context
    ChildComponent()
}

fn ChildComponent() -> impl IntoElement {
    let theme = use_context::<ThemeContext>();
    
    label().text(if theme.is_dark { "Dark Mode" } else { "Light Mode" })
}

Custom Hooks

Extract reusable logic:

// Returns (value, increment, reset)
fn use_counter(initial: i32) -> (i32, impl Fn(i32) + Copy, impl Fn() + Copy) {
    let mut count = use_state(|| initial);

    let increment = move |amount: i32| {
        *count.write() += amount;
    };

    let reset = move || {
        count.set(initial);
    };

    (*count.read(), increment, reset)
}

fn counter() -> impl IntoElement {
    let (count, increment, reset) = use_counter(0);

    rect()
        .child(label().text(format!("Count: {}", count)))
        .child(
            Button::new()
                .on_press(move |_| increment(1))
                .child(label().text("+"))
        )
        .child(
            Button::new()
                .on_press(move |_| reset())
                .child(label().text("Reset"))
        )
}

Use the Popup component for modal content:

fn modal() -> impl IntoElement {
    Popup::new()
        .background(Color::from_rgba(0, 0, 0, 0.5))
        .child(
             rect()
                .width(400.0)
                .height(300.0)
                .background(Color::WHITE)
                .child(label().text("Modal Content"))
        )
}

What’s Next

Next, I’ll learn about performance optimization.

Summary