Structure Makepad Apps with Async Backend Integration
Architecture patterns for Makepad/Robius apps, based on the Robrix and Moly codebases, for wiring sync UI to async Tokio backends.
Why it matters
Implement robust and scalable Makepad applications by adopting production-ready architectural patterns. This skill provides best practices for structuring your application, integrating asynchronous operations with Tokio, and managing complex data flows for production environments.
Outcomes
What it gets done
Structure Makepad applications using Robius framework best practices.
Integrate Tokio runtime for efficient asynchronous operations.
Implement patterns for async data loading and streaming results.
Manage sync/async communication patterns effectively.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-robius-app-architecture | bash Overview
Robius App Architecture Skill
Architecture patterns for structuring Makepad/Robius applications, based on the Robrix and Moly codebases, that connect a synchronous UI thread to an async Tokio backend via typed requests and a lock-free update queue. Use when building a Makepad app needing async backend integration or background-subscription-driven UI updates; adapt the request/update enums to the specific app.
What it does
This skill captures architecture best practices for structuring Makepad applications with async backend integration, drawn from two production Robius-framework codebases: Robrix, a Matrix chat client with complex sync/async background subscriptions, and Moly, a cross-platform (native and WASM) AI chat application with streaming APIs. The core pattern splits the app into a UI thread running Makepad's synchronous widget tree and a Tokio runtime handling async work, connected by a request/response channel: the UI calls submit_async_request(), which sends a typed AppRequest through an MPSC channel to a worker_task running in Tokio; the worker spawns an async task per request, and posts results back to the UI via Cx::post_action(). For high-frequency background updates, a separate lock-free path uses a crossbeam::SegQueue - background tasks push a DataUpdate onto the queue and call SignalToUI::set_ui_signal() to wake the UI thread, which then polls and drains the queue on Event::Signal. The skill documents the full app lifecycle: a live_design!-based app structure with an app_main! entry point and a #[derive(Live)] App struct holding a WidgetRef and app state, module registration order in live_register() (base widgets before shared widgets before feature modules), a startup sequence (init logging, init app data directory, load persisted window state, update UI visibility, start the async runtime), and a shutdown sequence triggered on Event::Shutdown that saves window geometry and app state.
Seven best practices anchor the pattern: keep UI logic on the main thread and async operations in the Tokio runtime; use typed enums for requests and actions rather than untyped messages; use crossbeam::SegQueue for high-frequency background updates; always call SignalToUI::set_ui_signal() after enqueueing an update; use Cx::post_action() to hand async task results back for UI handling; use Scope::with_data() to pass shared state through the widget tree; and register base widgets before dependent modules in live_register().
When to use - and when NOT to
Use this when building a Makepad application that needs async backend integration, designing sync/async communication patterns inside a Makepad app, or structuring a Robius-style application generally. It applies whether the async work is a single request/response call or a continuous background subscription feeding UI updates.
As with the skill's own stated limitations, treat this as architectural guidance rather than a drop-in library, use it only when the task matches this scope, and stop to ask for clarification if required inputs, permissions, or success criteria are missing - the patterns still need adapting to a specific app's request and update types.
Inputs and outputs
Inputs are the app's specific async operations (data fetches, message sends, streaming subscriptions) modeled as AppRequest variants and DataUpdate variants. Output is working Rust code implementing the UI-thread/Tokio-runtime split: the static runtime and request-sender initialization, the worker task's request-handling loop, and the widget-side signal-polling logic. Three production patterns for further detail live under _base/: 08-async-loading (async data loading with loading states), 09-streaming-results (incremental results via SignalToUI), and 13-tokio-integration (full Tokio runtime integration).
Integrations
Built on the Makepad UI framework and the Tokio async runtime, using crossbeam_queue::SegQueue for the lock-free update path. Three deeper reference files extend the pattern: tokio-integration.md and channel-patterns.md (from Robrix), and moly-async-patterns.md (from Moly), which covers the cross-platform helpers a native+WASM app needs beyond plain Tokio - a PlatformSend trait, a UiRunner for async defer operations, an AbortOnDropHandle for task cancellation, a ThreadToken for non-Send types on WASM, and a platform-agnostic spawn() function.
Who it's for
Rust developers building Makepad or Robius-framework desktop and cross-platform applications that need to integrate async backends - chat clients, streaming data apps, or anything requiring background subscriptions feeding a synchronous UI thread.
static PENDING_UPDATES: SegQueue<DataUpdate> = SegQueue::new();
/// Called from background async tasks
pub fn enqueue_update(update: DataUpdate) {
PENDING_UPDATES.push(update);
SignalToUI::set_ui_signal(); // Wake UI thread
}
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.