Pax puts the interface in a declarative file and the side effects in Rust
User interface engine driven by a declarative language + Rust
At a glance
- What is it?
- Pax is a Rust GUI framework where a .pax template declares the interface tree, layout, styling, events and motion while Rust owns state and handlers, with PAXEL expressions deriving values reactively. It targets macOS, iOS and the web from one codebase, and it is an alpha with an explicit limitations list.
- Who is it for?
- Adopt Pax if you are building a Rust application with a custom interface and you want the layout and styling to be declarative while the behaviour stays in ordinary Rust, particularly if you need one codebase for macOS, iOS and the web. Do not adopt it if you need a production-ready app store delivery today, because the README states that full macOS and iOS App Store delivery is not yet qualified.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two layers, and the split is grammatical
Pax is described as a language-first GUI framework for Rust, combining declarative UI authoring with Rust application logic and a portable, high-performance runtime for macOS, iOS, iPadOS and the web. The tagline is a language built for living interfaces.
The mechanism is a two-layer split. A .pax template declares the interface tree, layout, styling, events and motion, and its side-effect-free PAXEL expressions derive values from application state. Rust owns state changes, event handlers, platform integration, and other side effects.
The README puts the rule bluntly: Pax UI definitions are 100% declarative by grammatical constraint, relegating imperative work to the Rust layer. That is a language-level constraint rather than a style guideline, which means the compiler can reject an imperative construct in a template instead of leaving it to review.
The example in the README shows the loop in miniature. A .pax file declares a Group with percentage-based position, a pixel size, a rotation expression derived from a counter, and a click binding pointing at a Rust method.
<Group
x=50%
y=50%
width=240px
height=140px
rotate={(count * 4)deg}
@click=self.increment
>
<Text id=label text={"Clicks: " + count} />
</Group>The Rust side is a plain struct with a property and a handler.
use pax_kit::*;
pax_kit::runnable! { root: crate::Counter }
#[pax]
#[file("lib.pax")]
pub struct Counter {
pub count: Property<usize>,
}The `#[file("lib.pax")]` attribute is the link between them: it tells the macro which template belongs to this component. The `runnable!` macro declares the root component and generates the native launcher.
PAXEL expressions, and the spreadsheet analogy
The reactive model is the part that makes the declarative half viable, and the README describes it precisely.
When the handler updates count, Pax reactively reevaluates the expressions that depend on it and updates the running interface. Pax propagates the change through its reactive dependency graph, invalidating the affected layout and rendering work, including the minimal set of necessary GPU uploads when using a default GPU rendering backend.
The analogy the README offers is the spreadsheet, but instead of updating cells, Pax updates pixels. That is the useful way to hold the design in mind. In a spreadsheet you write a formula once and the system works out which cells to recompute. In Pax you write an expression once and the system works out which parts of the interface to redraw.
That has a direct consequence for how you write the code. A `rotate={(count * 4)deg}` expression is not a function call you invoke, it is a declaration of a dependency. The dependency graph is built from the expressions, so a property that nothing reads is not tracked, and a property that many things read is tracked once and fanned out.
The performance section says the same thing from the other side: the reactive graph limits updates to dependent properties and dirty layout and rendering work. So the cost of an update is proportional to what depends on the value that changed, not to the size of the interface. That is the argument for why the two-layer split works in practice rather than only in principle.
Rendering per target, and what composes with what
Pax aims to give you one Rust codebase for native and web. You build for web browsers, macOS, iOS and iPadOS from the same .pax templates and Rust application logic.
The rendering strategy is target-aware. Apple builds use Metal-backed rendering, while web builds select WebGPU where supported and fall back to CPU rendering where necessary.
Then there is the hybrid part, which is the most interesting design decision in the project. Pax uses platform-native text, form controls and scrolling where native behavior matters, then composites those elements with rendered content into one cohesive scene.
So the text input on a Mac is a real macOS text control, not a rectangle you drew, and the scrolling is the platform's, while the graphics around it are rendered by Pax. That is what makes the accessibility story plausible: native text and controls, selection and editing, and image alternatives provide accessibility foundations. The README is careful about the rest, saying broader accessibility work, including reading order, tab order, annotation and audit work, remains ongoing.
Being honest about the gap is the right call, because a custom rendered interface is exactly where accessibility tends to break, and the native-control hybrid is the mitigation. It is a partial mitigation today.
The repository layout matches the architecture. There are chassis directories for each platform, pax-chassis-common, pax-chassis-ios, pax-chassis-macos and pax-chassis-web, plus pax-compiler, pax-codegen, pax-engine, pax-gpu, pax-runtime, pax-std, pax-macro, pax-language, pax-language-server, pax-designtime and pax-cli.
Installing the CLI and running your first project
The quick start has two routes, and the AI-assisted one is unusual enough to mention.
For the traditional path you follow the Getting Started chapter in the docs. For the AI-assisted path the README says to prompt your coding agent naturally and include the primer URL, with the example prompt given as building a spaceship shooter game with the primer URL.
Then you run your first project, starting with the web target on macOS, Linux or Windows, with Rust and the web prerequisites installed.
cargo install pax-cli
pax-cli create my-first-project
cd my-first-project
pax-cli run --target webThe CLI scaffolds the project, builds the selected target, and serves the web app with template hot reload, and you open the printed URL. Pax macros compile the UI without an application `build.rs`, which is worth noting because it means there is no build script to wire up and no codegen step to run by hand.
The target flags are the same shape for the other platforms.
pax-cli run --target web --port 8080
pax-cli run --target macos # macOS and full Xcode
pax-cli run --target ios # macOS, Xcode and an iOS simulatorThe comments in the README state the prerequisites plainly, which saves you an afternoon of guessing. The macOS target needs macOS and full Xcode, the iOS target needs macOS, Xcode and an iOS simulator, and the web target is the one you can start with on any of the three desktop operating systems.
There are also cargo run and cargo build shortcuts. On macOS, cargo run runs the native app directly without a development server, and cargo build compiles without launching. To package production web output without designtime you use pax-cli build --release --target web. And from the repository itself, cargo run -p increment or cargo run --example increment runs the native macOS example.
The manifest makes the UI a library, not an application
The generated project manifest declares the UI as an rlib/cdylib and enables the launcher through its default standalone feature, and the README is explicit that the UI remains a separate library that another host can mount.
That is a small sentence with a large consequence. A typical desktop app couples the interface to the process that owns it. Here the interface compiles to a library with a lifecycle of its own, and the launcher is one possible host of it. The README points at the entry and mounting documentation for manifest details, other platforms and embedding.
The native launcher that ships with the scaffolded project is deliberately trivial.
fn main() -> Result<(), pax_kit::host::Error> {
pax_kit::init!()?.run()
}Three lines, and the whole of the platform bootstrapping is in the macro. That is the shape you would expect from a framework that wants the interface to be embeddable: the host contract is small enough that another host can implement it.
This is also the part that is least documented in the README itself. The entry and mounting chapter is in pax-docs, and the README does not describe the host interface, so if embedding into an existing native application is your goal, that document is the place to read rather than the overview.
Where Pax is the wrong choice today
Three cases, and the first is a direct statement in the README.
Full macOS and iOS App Store delivery is not yet qualified. That single sentence removes Pax from consideration for a consumer app you intend to ship this year, and it is the kind of qualifier that gets missed when a project is described as a cross-platform framework. You can build and run for those targets, and you can develop against them, but the last mile of distribution is not done.
The second is the maturity of the ecosystem. The repository has no GitHub releases, the version in Cargo.toml is 0.39.1 with publish set to false, and the dependency is by path within a workspace rather than from crates.io in the usual way. The workspace has more than thirty members, from pax-compiler and pax-codegen through pax-engine, pax-gpu, pax-runtime and the per-platform chassis. That is a lot of moving parts for a project at 0.x, and the directory listing includes pax-language-server, which tells you the editor tooling is part of the ambition.
The third is the workload shape. A reactive dependency graph that recomputes only what depends on a changed value is a good fit for a custom-drawn interface with many animated properties. It is a poor fit for a data-heavy form or a table view, where the work is not in the redraw but in the data. Pax is aimed at the first, and the README's own examples are graphical, from ink-and-light to liquid-glass to a game.
There is also the accessibility caveat worth repeating. The native-control hybrid gives real text and real form controls, but the README says reading order, tab order, annotation and audit work remains ongoing. For a public-facing product with a legal accessibility obligation, that is the gap to measure against your own requirements.
Editorial conclusion
Adopt Pax if you are building a Rust application with a custom interface and you want the layout and styling to be declarative while the behaviour stays in ordinary Rust, particularly if you need one codebase for macOS, iOS and the web. Do not adopt it if you need a production-ready app store delivery today, because the README states that full macOS and iOS App Store delivery is not yet qualified. Verify first which rendering backend your target selects, since Apple builds use Metal while web builds pick WebGPU where supported and fall back to CPU rendering otherwise.
Frequently asked questions
What is Pax?
Pax is a language-first GUI framework for Rust, combining declarative UI authoring with Rust application logic and a portable runtime for macOS, iOS, iPadOS and the web. A .pax template declares the interface tree, layout, styling, events and motion, while Rust owns state changes and side effects.
How do I create and run my first Pax project?
Run cargo install pax-cli, then pax-cli create my-first-project, cd into it and run pax-cli run --target web. The CLI scaffolds the project, builds the target and serves the web app with template hot reload, and Pax macros compile the UI without an application build.rs.
What are PAXEL expressions and how does reactivity work?
PAXEL expressions are side-effect-free expressions in .pax templates that derive values from application state. When a handler updates state, Pax reevaluates the dependent expressions through its reactive dependency graph, invalidating only the affected layout and rendering work, including the minimal set of GPU uploads.
Which rendering backends does Pax use per platform?
Rendering is target-aware: Apple builds use Metal-backed rendering, while web builds select WebGPU where supported and fall back to CPU rendering where necessary. Pax also uses platform-native text, form controls and scrolling where native behavior matters, compositing them with rendered content.
Is Pax ready for App Store delivery?
Not yet. The README states that full macOS and iOS App Store delivery is not yet qualified, though the macOS and iOS targets can be built and run for development with the appropriate Xcode and simulator setup.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/paxdotdev-pax)Was this useful?