# Crux: shared Rust core, thin native shell

> Crux splits a cross-platform app into a side-effect-free Rust core and a platform shell that runs the effects. It suits teams already willing to write Swift, Kotlin or TypeScript UI code around a Rust library.

**redbadger/crux** — Cross-platform app development in Rust

- Repository: https://github.com/redbadger/crux
- Website: https://redbadger.github.io/crux/
- Stars: 2,747 · Forks: 116
- Language: Rust
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/redbadger-crux

## The problem Crux solves: one behaviour, four UI toolchains

Most cross-platform frameworks make you choose between two bad options. Either you write the whole app once in a framework that renders its own widgets, and accept that the UI will not feel native, or you write the app three times and let the business logic drift between iOS, Android and web. Crux takes a third position: the behaviour lives once in Rust, and only the UI is written per platform.

The README is explicit about the intent. The shared core holds "business logic and behavior", while the shell is deliberately kept "as thin as it can be, with all other work done by the shared core". That is a real constraint, not a slogan. If your screens need heavy platform-specific interaction, animation or access to native APIs, that work sits in the shell and gains nothing from Crux.

The audience is narrower than the tagline suggests. You need engineers comfortable in Rust, and you still need people who can write SwiftUI, Jetpack Compose, React or Vue. Crux reduces duplication of logic, not duplication of UI skill.

## Managed effects: the mechanism that makes the core portable

The core is side-effect-free by construction. It cannot call an API, read a file or generate a random number directly. Instead it returns a Command describing the effects it wants, and the shell performs them.

The README states the reason plainly: because WebAssembly is one of the compilation targets, the core must remain side-effect-free "due to the sandboxed nature of the Wasm runtime environment". The same restriction that makes browser builds possible also makes the core immune to supply-chain attacks through external APIs, since it has no access to any.

The core defines four types, following the Elm architecture. Event is an enum of what the core can handle, Model is its internal state, Effect describes the side-effects it requests, and ViewModel is what the UI should display. They are tied together by an update function:

```rust
fn update(
    &self,
    event: Event,
    model: &mut Model,
) -> Command<Effect, Event> {
    // ...
}
```

update processes an event, mutates the model, and returns a Command. Effects support fire-and-forget, request/response and streaming semantics. Reusable effect interfaces are called Capabilities; Render is the only built-in one, and the repository contains others at various stages of maturity. That last phrase is worth taking literally: the capability set is not a complete standard library, and you may end up writing your own.

## Installing Crux and running the counter example

There is no CLI to install and no project generator documented in the README. The getting-started path is the book, then the examples in order, starting with counter. The workspace pins its toolchain through rust-toolchain.toml and declares rust-version = "1.90" in Cargo.toml, so a modern stable Rust is required.

Clone the repository and build the workspace crates:

```bash
git clone https://github.com/redbadger/crux.git
cd crux
cargo build -p crux_core
```

To see the core in isolation, run the counter example's tests. The README describes tests as just another shell, exercising the core the way a real app would, observing and resolving requested effects without fakes or mocks:

```bash
cargo test -p counter
```

For a platform build, the examples directory holds the shells. The counter-http example is the one to read when you need to talk to an API, and weather is the one that shows a fuller app. The examples README documents the per-example commands, including the Justfile targets used to build and run the iOS, Android and web shells.

One dependency detail matters before you start. The workspace pins facet_generate and facet-generate-attrs to a git revision rather than crates.io, with a comment explaining that a registry copy would resolve to a second, unrelated Attr type and fail generation with bad attribute format. If your build cannot reach that git remote, type generation for Swift, Kotlin and TypeScript will not work.

## What breaks, and when Crux is the wrong tool

The README carries a note that Crux is pre-1.0. It calls the project production-ready but says "occasional breaking changes to the API can be expected", with efforts to limit their extent and provide a gradual migration path. The release history backs this up: crux_core and crux_http both moved to 0.20.0 in August 2026, and 0.19.0 landed the month before. Anyone pinning to a 0.x line should plan for periodic upgrades.

The second limitation is architectural. Because the core cannot touch the outside world, every platform capability you need has to be expressed as an effect and implemented in each shell. Camera, notifications, background location, platform keychains: each one is a contract you define and then write three times. Crux gives you a place to put that contract, but it does not remove the work.

Crux is also the wrong choice when the app is single-platform. The core/shell split costs you a serialization boundary and a build step, and buys you nothing if there is only one shell. It is equally wrong when the UI is the hard part: Crux deliberately keeps the UI layer thin, so a design-heavy app with little shared logic will spend most of its effort in code Crux does not touch.

## Crux compared with Dioxus and Tauri

The two names that come up alongside Crux are Dioxus and Tauri, and the difference is where the UI lives.

Dioxus is a Rust UI framework. You write components in Rust and it renders them, on web through the DOM and on desktop and mobile through its own renderer. Crux does the opposite: it assumes you keep SwiftUI, Jetpack Compose, React or Vue, and generates the types and serialization code needed to call into Rust from them. If your team wants to write UI in Rust, Dioxus is the closer fit. If your team already has strong platform UI engineers and wants to stop rewriting logic, Crux fits.

Tauri is a desktop and mobile shell that hosts a web frontend and exposes Rust commands to it. The mental model is a web app with a Rust backend, and the boundary is command invocation. Crux's boundary is a message-based event and effect protocol with generated types, and its core is required to be pure, which is what allows the same core to run as a Wasm module in a browser and as a native library on iOS and Android. Tauri does not impose that purity, and does not need to.

## Licence, releases and the cost of staying current

Crux is Apache-2.0, declared in the workspace Cargo.toml and in the LICENSE file at the repository root. Apache-2.0 is permissive and includes an explicit patent grant, which matters for a library that gets linked into mobile binaries and shipped through app stores. The repository also carries a CODE_OF_CONDUCT.md and CONTRIBUTING.md. This is not legal advice; if your organisation has a policy on permissive licences or on the patent clause, run it past whoever handles that.

Upgrade cost is the practical concern. The crates are versioned independently, so crux_core and crux_http can move at different times, as they did in July and August 2026. A pre-1.0 API means migration notes matter more than release cadence. The repository contains a release.md file, and the pinned git dependencies for facet_generate mean an upgrade may involve moving a revision rather than only bumping a semver range.

The last push to the repository was on 2026-09-24, four days before this was written, so the project is being worked on. That says nothing about whether the next release will break your code.

## Testing without mocks, and why that changes the workflow

The most concrete claim in the README is about test speed. Because effects are values rather than calls, a test can act as a shell: it observes the effects the core requests, resolves them with whatever it wants, and checks the resulting model and view model. No fakes, mocks or stubs are needed for the effect boundary.

This is the design decision with the largest day-to-day payoff. A user journey that would otherwise need a device, a network stub and a UI driver becomes a Rust test that runs in the same cargo test invocation as everything else. The README frames this as high-level journey tests running in milliseconds rather than minutes or hours.

The catch is that this only covers the core. The shells still need their own testing, and the effect implementations in each shell are exactly the code the core tests cannot reach. Crux moves the testable surface, it does not eliminate the untestable one.

## Conclusion

Adopt Crux if you already have Rust engineers and are willing to keep native UI code in Swift, Kotlin or TypeScript; the counter, counter-http and weather examples are the fastest way to judge the split before committing. Do not adopt it if your team has no Rust experience and no appetite for the pre-1.0 migration path the README warns about, or if your app is a single-platform product where a shell would just be extra layers. Before writing real code, check the rust-version in the workspace Cargo.toml against your toolchain, and confirm that the version of facet_generate pinned by git revision resolves in your build.

## FAQ

### Is there a Rust framework for mobile app development?

Crux is one: it compiles a shared Rust core as a native static library on iOS and macOS and as a dynamic library on Android, with the UI written in SwiftUI or Jetpack Compose. The README lists the counter, counter-http and weather examples as the place to start.

### What does cross-platform app development mean in Crux's case?

In Crux it means sharing behaviour, not UI. The core holds the business logic and is compiled for each platform, while the shell stays thin and is written in the platform's own language.

### Can Rust build websites with Crux?

The core can run in a browser as a WebAssembly module, and the README lists React, Vue, Leptos and Yew as valid shells. The UI itself is not written in Rust unless you choose a Wasm-based framework such as Leptos or Yew.

## Sources

- [License: Apache-2.0](https://github.com/redbadger/crux/blob/master/LICENSE)
- [Project website](https://redbadger.github.io/crux/)
- [README](https://github.com/redbadger/crux/blob/master/README.md)
- [redbadger/crux on GitHub](https://github.com/redbadger/crux)
- [Releases](https://github.com/redbadger/crux/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/redbadger-crux
