leptoskit is a Rust toolkit for building an app's interface once, with leptos components and built-in Tailwind styles, and shipping it to the browser and as real native widgets on the desktop and on phones.
Get started Source on GitHub
What leptoskit is for# Most cross-platform toolkits make you pick: render to a web view everywhere, or write each platform twice. leptoskit takes a third path.
You describe a screen once, in Rust, as a tree of components: View, Text, Button, TextInput, ScrollView, and the rest. On the web, the same tree renders through leptos to the DOM, with server rendering and hydration. On native platforms, leptoskit's own layout engine lays the tree out with flexbox and CSS grid, and a platform backend commits it as the platform's real controls: AppKit views on macOS, UIKit on iOS, Android views, GTK 4 on Linux, Win32 controls on Windows, and, experimentally, ArkUI on HarmonyOS. In a terminal, the same tree draws as characters in the cell grid. Nothing runs in a web view.
A screen in leptoskit# A screen is a function returning an Element. Tailwind classes style it on every platform:
use leptoskit:: prelude:: * ;
# [ component ]
pub fn Welcome ( name : & str , on_start : impl Fn () + Send + Sync + ' static ) -> Element {
view! {
<View class="gap- 4 rounded- 2 xl bg-white p- 6 dark : bg-slate- 900 " >
<Text class="text-2xl font-bold" >{ format! ( "Hello, {name}" )} </Text >
<Text class="text-slate-500" >"Your interface, everywhere." </Text >
<Button class="rounded-lg bg-rose-500 px-4 py-2 text-white" on: press=on_start>
"Start"
</Button >
</View >
}
} The same function renders as DOM elements in a browser, and through a platform backend as NSViews, UIViews, Android views, GTK widgets, Win32 controls, or ArkUI components. The shared components guide walks through it, and the component catalog lists every component.
See it render# These screens are leptoskit element trees committed as real AppKit views and rendered offscreen. They are not mockups. See all of them .
Profile An avatar over the header and wrapping text measured with AppKit's font metrics.
Counter A centred card, a progress bar of nested flex boxes, and a pill button.
Chat Bubbles capped with max_width, aligned by align_self.How the pieces fit# The logo is the architecture. The ember boxes are layout: elements placed by the layout engine and committed as native widgets. The mint piece is the next version of your interface arriving over the air and dropping into place. Hover over it at the top of this page.
Crate What it does leptoskitThe crate your app depends on. It re-exports the components, styles, and runtime, with a feature per optional part. leptoskit_cliThe leptoskit command, also installed as lk, which scaffolds, builds, serves, tests, formats, and ships apps. leptoskit_nativeThe components, Tailwind support, and the renderer that diffs element trees into mutation batches for a platform backend. leptoskit_yogaThe layout engine behind native rendering: flexbox and CSS grid, gaps, aspect ratio, and right-to-left layout. leptoskit_appkitThe macOS backend. leptoskit_uikit , leptoskit_android , leptoskit_gtk , and leptoskit_windows do the same for their platforms. leptoskit_wasmThe on-device runtime. It runs the interface bundle in wasmi, an interpreter that needs no JIT. leptoskit_codepushOver-the-air updates: signed, content-hashed releases, binary deltas, and a client that verifies everything before it stages. leptoskit_axumThe server toolkit: static files with precompressed copies, SPA fallback, health checks, and the code push registry, on Axum. leptoskit_routerFile-based routing. src/routes/ becomes one typed Route enum. leptoskit_queryCached, deduplicated, retrying async data in the style of TanStack Query. leptoskit_l10nTyped translations in the style of Flutter's gen_l10n: ARB files become a Messages trait, with plurals for every language. leptoskit_testingWidget tests with semantic finders, plus a driver that runs the same journeys against real native apps. leptoskit_docsMarkdown with leptos components, and the generator that builds this site. See writing docs .
Over the air updates# An installed native app is a small host that runs your interface as a WebAssembly bundle. Code push ships a new bundle over the air: signed, content-hashed releases, binary deltas, and a client that verifies every byte before it stages the update. A trial release replaces the working one only after it renders, so a release that fails to start is rejected and the app keeps running. The generated iOS and Android apps install these releases today; the generated macOS, Windows, and Linux apps don't check for updates yet. A release must match the installed host's interface contract and signing policy, and store rules still apply to what a download changes. Code push covers keys, channels, publishing, and rollback.
Where things stand# leptoskit is young, and these docs say plainly what works today.
Native filesystem, SQLite, and secure-store plugins enforce aggregate storage budgets in addition to operation limits. See storage and networking for defaults and upgrade behavior.
Works now
The web pipeline: cached builds, wasm-opt, server rendering, islands, and Axum serving. The layout engine and the renderer. Native backends for macOS, iOS, Android, Linux, Windows, and the terminal that draw every built-in component, checked in CI on a real Mac, an iOS simulator, an Android emulator, a Linux desktop, a Windows desktop, and headless terminal sessions. Generated apps for all of those platforms. The Wasm runtime and code push, from a signed release to a verified, staged update on iOS and Android, optionally gated on independent source approvals. File routes, server functions, queries, translations, and the testing tools.
Known gaps
Generated Apple and Android apps draw the common controls, including Switch, Checkbox, Radio, Slider, LinearProgressIndicator, and ActivityIndicator. Custom widgets remain unsupported. Native controls expose their checked and range states, but other accessibility roles and states remain limited. Android system Back is not yet connected to the file router.iOS: iOS has no native checkbox or radio button, so Checkbox and Radio are drawn with UIButton. VoiceOver gets fewer roles than macOS, and no expanded or collapsed state.Android backend: a Custom widget kind with no registered native implementation stops the app instead of rendering an empty container. TalkBack gets a smaller set of roles than macOS.Linux: live regions update their text but are not announced, because announcements need GTK 4.14 and leptoskit supports GTK 4.6.Windows: Switch is drawn as a checkbox. Rounded corners are cut with a window region, so their edges are not smoothed, and borders stay rectangular inside them. Accessibility has no expanded or collapsed state. A Vector with no background takes clicks only where it has ink.Desktop shell plugin: invocation limits and cancellation clean up Unix process groups and Windows Job Objects. Unix commands that deliberately leave their group need additional host containment. Windows retains capacity when a member's termination cannot be confirmed; see the plugin inventory .Rive: shared Rive elements render on the web, macOS, iOS, Android, Linux, Windows, and experimental HarmonyOS. Native playback supports vector scenes and rejects images, fonts, text, layout, scripts, audio, view models, and unsupported draw features. View-model data binding is not exposed. HarmonyOS playback still needs validation on the target device.Motion: motion animates elements on the web only. macOS, iOS, Android, Windows, Linux, the terminal, and HarmonyOS show every element at its resting pose, and scroll-driven motion needs a browser with CSS scroll timelines; others show the resting pose too.Runtime plugins on Windows, Linux, and the terminal: the generated apps don't register runtime plugins yet. Plugins listed in native.plugins are left out with a warning, and calls to them fail with Unavailable. See plugins a platform can't run .HarmonyOS (experimental): eight components (not including Vector), synchronous Rust plugins and ArkTS plugins (fold posture is the only ArkTS one so far) but not worker or async plugins, italic text and per-edge borders through ArkUI, and no code push, dark mode, right-to-left layout, active: pressed styles, or CI run on a device. See HarmonyOS .Terminal: the terminal's font is the user's, so font sizes and weights draw as attributes and colors rather than different metrics, Image and Vector draw a placeholder texture, Rive playback is unsupported, and right-to-left text is not reordered. See Terminal .Browser extensions: extensions build and package for Chromium, Firefox, and Safari, whose extension goes inside the macOS and iOS apps. leptoskit dev and leptoskit test extension run them in Chrome for Testing or Firefox (--browser firefox). leptoskit publish uploads extensions to the Chrome Web Store, Edge Add-ons, and addons.mozilla.org, and apps to App Store Connect and Google Play. iOS apps carry their Safari extension; Mac App Store submission still needs a sandboxed host.DevTools: under leptoskit dev, the web page and the generated macOS, iOS, and Android apps report to DevTools . The native apps outline the selected element and pick whatever you tap, but don't name each element's component yet. Windows, Linux, the terminal, and HarmonyOS don't report yet.Scroll offsets: scroll_offset scrolls a list from code on the web and in the generated macOS, iOS, and Android apps. The native backend crates, and so the generated Windows and Linux apps, HarmonyOS, and the terminal don't apply it yet.Plugins on Linux, Windows, and the terminal: the generated runners can't host plugins yet. They build without the ones leptoskit.toml grants, and say so, so sign-in there answers that it isn't set up: its client needs the http, secure-store, passkeys, and web-auth plugins. Native apps also have no timer for the query cache yet, so retries there don't wait between attempts.Sign-in: email codes, authenticator apps (TOTP), recovery codes, step-up, required two-step verification, passkeys, opt-in passwords, the twenty built-in providers (Google, Apple, Microsoft, GitHub, Facebook, X, Discord, LinkedIn, GitLab, Slack, Twitch, Spotify, Reddit, Amazon, Dropbox, Bitbucket, Notion, Zoom, TikTok, and Yahoo), and any OpenID Connect provider work on the web and in the generated iOS, Android, and macOS apps. The apps use the system's own passkey sheet, Sign in with Apple's own sheet on Apple devices, and the system browser for other providers. In the apps, passkeys aren't offered in a text field's suggestions; Android signs in with providers only when its default browser has Auth Tab (Chrome 137 or later); and Apple devices need the app's associated domain, which the generated Xcode projects don't add yet. Windows and Linux have no native passkeys: a host that registers plugins itself gets provider sign-in in the default browser, and passkey calls answer unsupported. HarmonyOS has neither. Solana wallets link and sign in through the Wallet Standard on the web only; native apps list, rename, and unlink linked wallets, and answer unsupported for asking a wallet to sign until a native wallet adapter exists. Wallet proofs are message signatures, so program-derived addresses, multisigs, and wallets that can't sign messages can't be linked yet. leptoskit generate auth includes masked PasswordInput fields when password sign-in is enabled. Merging accounts can bring the other account's data along once every table that holds it declares an on_account_merge strategy, which leptoskit db check enforces.Styled text, key handlers, and controlled focus work on the web and in the terminal. The native widget backends still draw a Text's styled runs in its own single style, deliver no on_key presses, and do not move focus for focused(true). Italic and per-edge borders (border-l-2, border-t, …) paint on Linux (GTK), Android, and experimental HarmonyOS (ArkUI); AppKit, UIKit, and Win32 still draw italic upright and leave a per-edge border undrawn.On every native platform, Image loads local files only, not URLs. Code push updates reach the iOS and Android apps; the generated desktop apps don't check for them yet. CSS shadows, transforms, gradients, and custom fonts aren't supported anywhere yet: the CSS coverage reference lists what is. Color and length var(--name) utilities work on the web and, through Theme, on native — including one nested fallback, /opacity on color vars, and [theme] tokens in leptoskit.toml. Deeper nesting and Tailwind @theme / tailwind.config files still don't. Rive's internal vector paints and transforms are separate from those CSS properties. Platforms has the full picture.
Axum is the one supported server.
To work on leptoskit itself, start with CONTRIBUTING.md .