Day: create native apps for every platform under the sun from a single Rust codebase

Day is a Rust framework that builds your app for Android, iOS, HarmonyOS, Windows, macOS, Linux, and the web — with each platform’s own native controls. Your product looks and works the way each platform’s users expect.

cargo install day-cli

Installs the day CLI from crates.io. Requires a stable Rust toolchain (rustup).

Runs natively on

macOS

AppKit

macos-appkit

The Mac’s native toolkit: the same NSButton, NSTableView, and menu bar that Finder and Mail are made of. Packs as a signed, notarized .dmg.

Building for macOS

iOS & iPadOS

UIKit

ios-uikit

Apple’s UI framework for iPhone and iPad: real navigation pushes, tab bars, and system text handling. Ships as a normal .ipa.

Building for iOS & iPadOS

Android

Material Components

android-mdc

Google’s native widgets over android.view, on phones, tablets, and foldables, themed by the OS and packaged as .apk or .aab.

Building for Android

Linux

GTK 4

linux-gtk

GNOME’s toolkit, styled by libadwaita. It is the desktop Ubuntu, Fedora, and Debian install by default. Ships as a flatpak.

Building for Linux

Linux

Qt 6 Widgets

linux-qt

The toolkit beneath KDE Plasma: the desktop of Kubuntu, openSUSE, and the Steam Deck. Portable enough that it is also a Day target on macOS and Windows.

Building for Linux

Windows

XAML

windows-xaml

Windows 11’s native controls, hosted in a Win32 window with no framework runtime to install. Packs as .msix plus an installer.

Building for Windows

HarmonyOS

ArkUI

harmony-arkui

HarmonyOS Next’s native UI framework, driven through its C node API on Huawei phones and tablets. Packs as a .hap.

Building for HarmonyOS

What native means here

Native on every platform, from one codebase.

A Day app is made of the platform's own controls. The button you write in Rust becomes an NSButton on macOS, a UIButton on iOS, and aMaterialButton on Android — the same objects an app written in Swift, Kotlin, or ArkTS creates. Attach Accessibility Inspector or Xcode's view debugger to a running Day app and those are the classes you find. Because those are the platform's controls, text input, scrolling physics, and screen-reader support are the platform's too, and they improve with every OS update.

  • The platform's widgets, instantiated: NSButton and NSTableView on macOS, UIButton and UITableView on iOS, MaterialButton on Android, and the matching controls on GTK, Qt, XAML, and ArkUI.
  • Behavior you never wrote: input methods, spellcheck, scrolling physics, text selection, focus order, and VoiceOver or TalkBack come from the OS.
  • Packaged the platform's way: day pack produces a signed and notarized .dmg, an .ipa, an .aab, an .msix, a flatpak, or a .hap — whatever that platform's users install.

One codebase, every platform

One Rust function. A native app everywhere.

This is a complete, working counter, the whole thing. The same function becomes a real AppKit control on macOS, UIKit on iOS, Material on Android, and native GTK, Qt, XAML, and ArkUI widgets everywhere else. Those are the screenshots in the carousel above: real platform components, from this exact kind of code.

  • Just Rust: functions and builder calls in the same language as your logic and tests.
  • Reactive by binding: change a signal and only the control showing it updates — one native setter call, not a re-render.
  • Native on the other side: every widget it produces is one the platform itself provides.
counter.rs
use day::prelude::*;

fn counter() -> impl Piece {
    let count = Signal::new(0i64);
    column((
        label(move || format!("{} clicks", count.get())),
        row((
            button("").action(move || count.update(|c| *c -= 1)),
            button("+").action(move || count.update(|c| *c += 1)),
        )).spacing(8.0),
    ))
    .spacing(12.0)
    .padding(16.0)
}

One function, a native counter on all twelve targets.

Why Day

Native quality and a single codebase.

Cross-platform tools usually make you choose between apps that feel native and a codebase you only write once. Day keeps both.

Apps your users recognize

Every control is the platform’s own: a real Mac button on macOS, real Material on Android. Text, scrolling, shortcuts, and dark mode behave the way each platform’s users already expect, and keep improving with OS updates you never have to ship.

Milliseconds to launch, megabytes to install

Day apps are compact native binaries that link the toolkit the operating system already has, so they start in milliseconds and install in megabytes.

One codebase, one team

Six operating systems and the web from a single Rust program. A feature lands once and ships everywhere at the same time.

Accessible and localized from the first build

Screen-reader support uses each platform’s own accessibility tree, and every user-facing string is localized with Mozilla Fluent. Both are in the framework from the first build.

Tested on every platform, every change

Each commit builds a full demo app on all twelve targets, drives it end to end, and captures screenshots. The gallery on this site is those exact artifacts.

One CLI for the whole pipeline

One command line creates, runs, tests, and packages signed installers for every platform: dmg, App Store, Android, flatpak, Windows. The same automation scripts drive the app everywhere, for CI and for AI-assisted development.

Built for the desktop too

Menu bar, keyboard shortcuts, a preferences window, toolbars, and multi-window are part of the framework, and the Window menu on macOS assembles itself.

Drop down to the platform

When you need the platform’s own UI framework, use it: SwiftUI views embed in the Day tree on Apple platforms with typed Rust constructors, and per-platform tweaks reach any native widget’s API, so the platform’s full toolkit stays within reach.

From test run to store listing

One dayscript walkthrough is an end-to-end test, an accessibility pass, and your screenshot shoot — per locale and theme. The captures feed the store listings in store/ and a generated app website, so shipping collateral stays as current as the build.

Agents & automation

Built for agents and CI.

Every Day app embeds dayscript, the same automation engine that drives Day's own release pipeline. A YAML script, an agent over MCP, or your CI taps buttons, types text, asserts what's on screen by stable element ids, and captures screenshots, identically on every platform.

  • Agents that see their work: Claude Code and other MCP clients get the day tools (build, relaunch, drive, screenshot), so an LLM edits your app, drives the running build, and shows you pixels to prove the change landed.
  • One script, twelve targets: run the walkthrough per theme and locale and the same YAML produces your localized screenshots. Thegallery on this site is exactly that.
  • Regressions caught on every push: the walkthrough runs in CI, and a failed assertion or a blank capture fails the build before it reaches a user.

Evaluating your options

Four ways to ship one app everywhere.

Web-view shells

Electron, Tauri

Your interface is a web app, so a web team is productive from the first day and the npm UI ecosystem is available.

Custom renderers

Flutter, Iced

The framework draws every pixel itself, so screens are identical on every platform and a design system carries over exactly.

Per-platform native

Swift + Kotlin + C++ teams

Each app uses its platform’s full toolkit directly, with one codebase and one team per platform.

Single-platform today

Just a Mac app? Just Linux?

A Day app on one platform is a native app on that platform, with native packaging and scriptable testing. A second platform later is one command, day app add-toolkit.

Day keeps the platform's own components and the single codebase. It is young, it relaunches rather than hot-reloads, and it leaves pixel-level branding to the platform.

Read the comparison →

How it works

When data changes, the right control updates.

Day builds your interface from real platform components once, then keeps each one connected to your application's state. A change runs only the closures that read it and ends in one native setter call. Day apps perform like hand-written platform code because, at runtime, that's what they are.

  • Plain Rust: functions and builder calls in the same language as the rest of your app.
  • Native navigation: sidebars, tabs, and back-gestures are the platform's own containers.
  • Testable end to end: one automation script drives the app identically on every platform.
navigation.rs
let section = Signal::new(String::new());
selector(section)
    .style(SelectorStyle::Sidebar)   // native split view / tabs / pushing list
    .item("home",     "Home",     home_page)
    .item("settings", "Settings", settings_page)

Your first Day app is ten minutes away.