Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Augani/kael/llms.txt

Use this file to discover all available pages before exploring further.

Kael’s context system is the backbone of its reactive model. Rather than passing App everywhere, different parts of the framework receive purpose-built context objects — Context<T>, async contexts, and window contexts — all sharing a common set of traits. This page documents the three core traits (AppContext, VisualContext, BorrowAppContext), the EventEmitter<E> contract, and the Reservation<T> primitive that lets you obtain an entity ID before the entity is constructed.

AppContext trait

AppContext is the shared interface that App, Context<T>, AsyncApp, and AsyncWindowContext all implement. Any function that accepts impl AppContext or C: AppContext works with all of them.
pub trait AppContext {
    type Result<T>;

    fn new<T: 'static>(
        &mut self,
        build_entity: impl FnOnce(&mut Context<T>) -> T,
    ) -> Self::Result<Entity<T>>;

    fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>>;

    fn insert_entity<T: 'static>(
        &mut self,
        reservation: Reservation<T>,
        build_entity: impl FnOnce(&mut Context<T>) -> T,
    ) -> Self::Result<Entity<T>>;

    fn update_entity<T, R>(
        &mut self,
        handle: &Entity<T>,
        update: impl FnOnce(&mut T, &mut Context<T>) -> R,
    ) -> Self::Result<R>
    where
        T: 'static;

    fn read_entity<T, R>(
        &self,
        handle: &Entity<T>,
        read: impl FnOnce(&T, &App) -> R,
    ) -> Self::Result<R>
    where
        T: 'static;

    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
    where
        F: FnOnce(AnyView, &mut Window, &mut App) -> T;

    fn read_window<T, R>(
        &self,
        window: &WindowHandle<T>,
        read: impl FnOnce(Entity<T>, &App) -> R,
    ) -> Result<R>
    where
        T: 'static;

    fn background_spawn<R>(
        &self,
        future: impl Future<Output = R> + Send + 'static,
    ) -> Task<R>
    where
        R: Send + 'static;

    fn read_global<G, R>(
        &self,
        callback: impl FnOnce(&G, &App) -> R,
    ) -> Self::Result<R>
    where
        G: Global;
}

Entity lifecycle

cx.new(build)
(FnOnce(&mut Context<T>) -> T) -> Entity<T>
Creates a new entity and stores it in the app. The closure receives a Context<T> and must return a value of type T. The returned Entity<T> is a lightweight, reference-counted handle.
let counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
cx.reserve_entity()
() -> Reservation<T>
Reserves a slot for an entity without constructing it yet. The returned Reservation<T> exposes the future EntityId so you can reference the entity (e.g. in a parent entity) before the entity itself exists.
cx.insert_entity(reservation, build)
(Reservation<T>, FnOnce(&mut Context<T>) -> T) -> Entity<T>
Completes the reservation by constructing the entity. Must be called with the Reservation returned by the same context’s reserve_entity. Fails to compile if reservation was obtained from a different context instance.
cx.update_entity(handle, update)
(&Entity<T>, FnOnce(&mut T, &mut Context<T>) -> R) -> R
Runs the closure with mutable access to the entity’s data and a Context<T>. Call cx.notify() inside the closure to mark the entity dirty and trigger re-renders.
cx.update_entity(&counter, |counter, cx| {
    counter.count += 1;
    cx.notify();
});
cx.read_entity(handle, read)
(&Entity<T>, FnOnce(&T, &App) -> R) -> R
Provides read-only access to the entity’s data alongside a shared &App.

Window access

cx.update_window(handle, f)
(AnyWindowHandle, FnOnce(AnyView, &mut Window, &mut App) -> T) -> Result<T>
Runs f with mutable access to the window’s root view, its Window, and the App. Returns Err if the window has been closed.
cx.read_window(handle, read)
(&WindowHandle<T>, FnOnce(Entity<T>, &App) -> R) -> Result<R>
Provides read-only access to the window’s root entity. Returns Err if the window has been closed.

Background tasks

fn background_spawn<R>(
    &self,
    future: impl Future<Output = R> + Send + 'static,
) -> Task<R>
where
    R: Send + 'static;
Spawns a Send future on the background thread pool. The returned Task<R> must be held or .detach()ed; dropping it cancels the task.
let task = cx.background_spawn(async move {
    fetch_data().await
});
// task is dropped here — fetch_data is cancelled unless you store it
If you need to update UI after the background work completes, pass a clone of the relevant Entity<T> into the async closure and call .update() on it inside the future.

Reading globals

fn read_global<G, R>(
    &self,
    callback: impl FnOnce(&G, &App) -> R,
) -> Self::Result<R>
where
    G: Global;
Reads a global value through a callback. On synchronous contexts (App, Context<T>), the Result type is the return value directly. On async contexts, it is Result<R>.
let theme = cx.read_global::<Theme, _>(|theme, _app| theme.name.clone());

Reservation<T>

Reservation<T> is returned by reserve_entity and passed to insert_entity. Its sole purpose is letting you obtain an EntityId before the entity is built.
pub struct Reservation<T>(pub(crate) Slot<T>);

impl<T: 'static> Reservation<T> {
    pub fn entity_id(&self) -> EntityId;
}
let reservation: Reservation<MyView> = cx.reserve_entity();
let id = reservation.entity_id(); // use id to wire up references

// Later — complete the entity
let entity = cx.insert_entity(reservation, |_cx| MyView::new(id));
Reservation<T> is not Clone. You can only complete it once via insert_entity.

VisualContext trait

VisualContext extends AppContext with methods that require a window to be present. It is implemented by window contexts such as the callback arguments inside WindowHandle::update and open_window.
pub trait VisualContext: AppContext {
    fn window_handle(&self) -> AnyWindowHandle;

    fn update_window_entity<T: 'static, R>(
        &mut self,
        entity: &Entity<T>,
        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
    ) -> Self::Result<R>;

    fn new_window_entity<T: 'static>(
        &mut self,
        build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
    ) -> Self::Result<Entity<T>>;

    fn replace_root_view<V>(
        &mut self,
        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
    ) -> Self::Result<Entity<V>>
    where
        V: 'static + Render;

    fn focus<V>(&mut self, entity: &Entity<V>) -> Self::Result<()>
    where
        V: Focusable;
}
cx.window_handle()
() -> AnyWindowHandle
Returns the type-erased handle to the window associated with this context.
cx.update_window_entity(entity, update)
(&Entity<T>, FnOnce(&mut T, &mut Window, &mut Context<T>) -> R) -> R
Like update_entity, but also provides mutable access to the Window. Use this when your entity update needs to interact with window-level APIs — inserting hitboxes, dispatching actions, or reading layout.
cx.new_window_entity(build)
(FnOnce(&mut Window, &mut Context<T>) -> T) -> Entity<T>
Creates a new entity with access to both Window and Context<T>. Use this when construction requires window-level information such as the current scale factor or focus state.
cx.replace_root_view(build)
(FnOnce(&mut Window, &mut Context<V>) -> V) -> Entity<V>
Replaces the window’s root view with a new view of type V. The old root view and all its descendants are dropped. Requires V: Render.
window_handle.update(cx, |_old, window, cx| {
    cx.replace_root_view(|window, cx| {
        cx.new_window_entity(|window, cx| MyNewView::new(window, cx))
    });
})?;
cx.focus(entity)
(&Entity<V>) -> () where V: Focusable
Moves keyboard focus to the element associated with entity. The entity must implement Focusable, which means it must expose a FocusHandle via fn focus_handle(&self, cx: &App) -> FocusHandle.
cx.focus(&my_input_entity);

invalidate_cache

fn invalidate_cache(&mut self, element_id: impl Into<ElementId>) -> Result<()>
Invalidates the retained subtree cache for elements with the given ID in the current window. Call this when you know that an element’s cached layout or paint is stale without having called cx.notify().

BorrowAppContext trait

BorrowAppContext is auto-implemented for any type that implements BorrowMut<App>, including App, Context<T>, and async contexts. It provides three global-mutation helpers as a convenience over the lower-level App methods.
pub trait BorrowAppContext {
    fn set_global<T: Global>(&mut self, global: T);

    fn update_global<G, R>(
        &mut self,
        f: impl FnOnce(&mut G, &mut Self) -> R,
    ) -> R
    where
        G: Global;

    fn update_default_global<G, R>(
        &mut self,
        f: impl FnOnce(&mut G, &mut Self) -> R,
    ) -> R
    where
        G: Global + Default;
}
cx.set_global(value)
(G: Global)
Stores value as the app-wide global for type G. Replaces any existing value and notifies all observe_global::<G>() subscribers.
cx.update_global(f)
(FnOnce(&mut G, &mut Self) -> R) -> R where G: Global
Temporarily removes the global from the app, calls f with a mutable reference to it and &mut Self, then restores it. The closure can call other context methods (e.g., open a window) while mutating the global.
cx.update_global::<AppState, _>(|state, cx| {
    state.logged_in = true;
    cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| Dashboard)).ok();
});
Panics if the global of type G has not been set. Use update_default_global if the global may be absent.
cx.update_default_global(f)
(FnOnce(&mut G, &mut Self) -> R) -> R where G: Global + Default
Like update_global, but inserts a G::default() value first if the global is not yet present. Safe to call unconditionally.
cx.update_default_global::<FeatureFlags, _>(|flags, _cx| {
    flags.enable("dark_mode");
});

EventEmitter<E> trait

EventEmitter<E> is a marker trait that declares which event type E a given entity can emit. There is no method to implement — the trait body is empty.
pub trait EventEmitter<E: Any>: 'static {}
Once implemented, other entities can subscribe to events using Context::subscribe. The event is dispatched by calling cx.emit(event) inside the entity’s Context<T>.
pub struct CountChanged {
    pub new_count: usize,
}
An entity can implement EventEmitter<E> for multiple event types E. Each subscription and emission is type-checked at compile time.

Combining traits in practice

The following example shows how AppContext, VisualContext, and BorrowAppContext interact in a typical window open/update flow:
use kael::{App, AppContext, Application, BorrowAppContext, Global, WindowOptions};

struct AppTheme { dark: bool }
impl Global for AppTheme {}

struct RootView;
impl kael::Render for RootView {
    fn render(&mut self, _window: &mut kael::Window, _cx: &mut kael::Context<Self>) -> impl kael::IntoElement {
        kael::div()
    }
}

Application::new().run(|cx: &mut App| {
    // BorrowAppContext::set_global — available because App: BorrowMut<App>
    cx.set_global(AppTheme { dark: true });

    // AppContext::new — create an entity
    let _root = cx.new(|_cx| RootView);

    // App::open_window — open a window using VisualContext inside the callback
    cx.open_window(WindowOptions::default(), |_window, cx| {
        // VisualContext::new_window_entity — entity with Window access
        cx.new_window_entity(|_window, _cx| RootView)
    }).unwrap();

    // BorrowAppContext::update_global — mutate across the full lifetime
    cx.update_global::<AppTheme, _>(|theme, _cx| {
        theme.dark = false;
    });
});

Build docs developers (and LLMs) love