Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Context

Context provides a way to share app-wide state (config, theme, services) across widgets without passing values through every level of the widget tree.

When to Use Context

Use context for cross-cutting concerns that many widgets need:

  • Application configuration
  • Theme or styling data
  • Service handles (loggers, API clients)
  • User preferences

For state that only a few nearby widgets share, passing signals directly is simpler and preferred.

Providing Context

Call provide_context in your App::run() setup to make a value available everywhere:

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

App::new().run(|app| {
    provide_context(Config::load());

    app.add_surface(config, || build_ui());
});
}

Retrieving Context

use_context (fallible)

Returns Option<T> — useful when the context is optional:

#![allow(unused)]
fn main() {
if let Some(cfg) = use_context::<Config>() {
    println!("threshold: {}", cfg.warn_threshold);
}
}

expect_context (infallible)

Panics with a helpful message if the context was not provided:

#![allow(unused)]
fn main() {
let cfg = expect_context::<Config>();
}

with_context (zero-clone)

Borrows the value without cloning — ideal for large structs when you only need one field:

#![allow(unused)]
fn main() {
let threshold = with_context::<Config, _>(|cfg| cfg.cpu.warn_threshold);
}

has_context (existence check)

Check if a context has been provided without retrieving it:

#![allow(unused)]
fn main() {
if has_context::<Logger>() {
    expect_context::<Logger>().info("ready");
}
}

Reactive Context

For mutable shared state, store a Signal<T> as context. This is the most powerful pattern — any widget reading the signal during paint/layout auto-tracks it for reactive updates.

provide_signal_context

Creates an RwSignal and provides it as context in one step:

#![allow(unused)]
fn main() {
App::new().run(|app| {
    // Creates RwSignal<Theme> and stores it as context
    let theme = provide_signal_context(Theme::default());

    app.add_surface(config, || build_ui());
});
}

Reading a signal context

#![allow(unused)]
fn main() {
fn themed_box() -> Container {
    let theme = expect_context::<RwSignal<Theme>>();

    container()
        .background(move || theme.get().bg_color)
        .child(text(move || theme.get().title.clone()))
}
}

When the signal is updated anywhere, all widgets reading it automatically repaint.

Combining with SignalFields

For config structs with many fields, use #[derive(SignalFields)] with context so each widget only repaints when the specific field it reads changes:

#![allow(unused)]
fn main() {
#[derive(Clone, PartialEq, SignalFields)]
pub struct AppConfig {
    pub cpu_warn: f64,
    pub mem_warn: f64,
    pub title: String,
}

App::new().run(|app| {
    let config = AppConfigSignals::new(AppConfig {
        cpu_warn: 80.0,
        mem_warn: 90.0,
        title: "My App".into(),
    });
    provide_context(config);

    app.add_surface(surface_config, || build_ui());
});

// In a widget — only repaints when cpu_warn changes
fn cpu_indicator() -> Container {
    let config = expect_context::<AppConfigSignals>();
    let threshold = config.cpu_warn;  // Signal<f64>

    container()
        .background(move || {
            if current_cpu() > threshold.get() {
                Color::RED
            } else {
                Color::GREEN
            }
        })
}
}

Context vs Passing Signals

ApproachBest for
Pass signals directlyParent-child, 1-2 levels deep, few consumers
ContextApp-wide state, many consumers across modules

Since Signal<T> is Copy, passing them directly is zero-cost. Context adds a Vec scan + downcast, which is negligible but unnecessary when only a few widgets need the value.

API Reference

#![allow(unused)]
fn main() {
// Store a value (one per type, replaces if exists)
pub fn provide_context<T: 'static>(value: T);

// Retrieve (clones)
pub fn use_context<T: Clone + 'static>() -> Option<T>;
pub fn expect_context<T: Clone + 'static>() -> T;

// Borrow without cloning
pub fn with_context<T: 'static, R>(f: impl FnOnce(&T) -> R) -> Option<R>;

// Existence check
pub fn has_context<T: 'static>() -> bool;

// Create signal + provide as context
pub fn provide_signal_context<T: Clone + PartialEq + Send + 'static>(
    value: T
) -> RwSignal<T>;
}