Cross-platform
Truly cross-platformThe same Rust code renders natively on phones, desktops, the browser, a GPU surface, even a terminal. Not a fan of the implementation decisions of a particular platform? Your niche target doesn't have a premade implementation? Implementing one trait is all it takes to add a new backend and get the rest of the ecosystem for free.
One author treeYou write components against a single vocabulary of primitives — `View`, `Text`, `Button`, `ScrollView`, and the rest — plus signals for state. That tree knows nothing about the platform it will run on. The CLI handles the per-target build pipeline and wrapper; your code stays platform-agnostic.There's no "web version" and "mobile version" of a screen to keep in sync. The branching you'd normally write by hand — different components, different layout rules, different event models per platform — is absorbed below the primitive layer.
// One component. No `#[cfg(target_os)]`, no platform branches.
#[component]
fn app() -> Element {
    let count = signal!(0);
    ui! {
        view {
            text { format!("Taps: {}", count.get()) }
            button(
                label = "Tap".to_string(),
                on_click = move || count.update(|n| *n += 1),
            )
        }
    }
}

// Ship the SAME function to every target:
//   idealyst run ios        → UIKit
//   idealyst run android    → Android Views
//   idealyst dev --web      → WASM + DOM
//   idealyst run macos      → AppKit
Native widgets, not a webviewA `Button` is a real `UIButton` on iOS, a real Android button view over JNI, an `NSButton` on macOS, and a `<button>` in the DOM. A `ScrollView` is a real `UIScrollView` with native scroll physics and bounce, a real Android scroll container, an `NSScrollView` on macOS. The framework drives the platform's own toolkit — it does not ship a renderer that imitates one.That means the things users feel without thinking about — momentum scrolling, text selection, the system back gesture, accessibility focus, keyboard handling — are the platform's real implementations, not approximations. The app reads as belonging to the device it's running on.Where a target has no native toolkit to drive — a bare GPU surface, a microcontroller's framebuffer, a terminal grid — the framework renders the primitives itself through that backend. Same primitives, different bottom layer.
The same behavior everywhereBackends diverge in mechanism but converge in observable behavior. A scale animation uses `UIView.transform` on iOS, a `CALayer` transform on macOS, and a CSS `transform` on web — three different mechanisms, one identical visual result. The Backend trait is where the toolkit differences get absorbed.This is a deliberate design rule, not an accident: there are no per-platform fudge factors in framework code — no "0.95 scale on iOS but 0.93 on Android because the renders differ." If a primitive looks or behaves differently on one backend, that backend is fixed at its root so every target benefits, rather than the call site being patched to paper over it.The payoff for you: what you verify on the web preview is what ships on the phone. The platform you happen to be developing on isn't special.
The Backend trait is the only seamEvery platform is one implementation of the `Backend` trait. The trait is the framework's single seam to the outside world — it knows about primitives (create / update / insert / remove), style application, layout, refs, and animated values, and nothing higher-level. Routing, theming, components, and reactivity all sit above it and work unchanged on any backend that satisfies the contract.So "truly cross-platform" isn't a fixed list of blessed targets. It's an open contract: get the primitive surface right for a new surface — a proprietary display, a server-side renderer, a games console — and everything the framework already does comes along for free.Peripheral, platform-specific capabilities (maps, video, web views) don't bloat that core contract either; they plug in as third-party extensions through `Element::External` and a per-backend registry.
// Adding a new platform = implementing one trait.
impl Backend for MyBackend {
    fn create_view(&mut self, ...) -> NodeId { ... }
    fn create_text(&mut self, ...) -> NodeId { ... }
    fn insert(&mut self, parent: NodeId, child: NodeId, ...) { ... }
    fn apply_style(&mut self, node: NodeId, ...) { ... }
    // ...one method per primitive, plus layout / refs / animated values
}
See every targetThe full list of platforms idealyst runs on — phones, desktops, browsers, GPU surfaces, embedded devices, the terminal — lives on the Targets page. The per-primitive implementation status for each backend (what's working, in progress, or planned) lives on the Backends page.Browse every target →See the Backends matrix →
On this page
One author tree
Native widgets, not a webview
The same behavior everywhere
The Backend trait is the only seam
See every target
IdealystOne codebase, native everywhere.
© Idealyst 2026