waterui 0.2.0

A modern UI framework for Rust
Documentation

WaterUI

Crates.io docs.rs License Coverage

A modern, cross-platform UI framework for Rust that renders to native platform widgets (UIKit/AppKit on Apple, Android View on Android) rather than drawing its own pixels. Build reactive, declarative UIs with Rust's type safety and performance.

Overview

WaterUI combines declarative, component-based architecture with fine-grained reactivity to deliver truly native user interfaces across platforms. Unlike traditional Rust UI frameworks that implement custom rendering, WaterUI translates your Rust view tree into platform-native UI components through an FFI bridge, ensuring your apps look and feel native on every platform.

The framework is built on three core principles:

  • Native-first rendering: Your UI components compile to UIKit/AppKit views on iOS/macOS and Android View on Android, delivering authentic native behavior and performance.
  • Fine-grained reactivity: Powered by the nami crate, UI updates are surgical and automatic—only affected components re-render when state changes.
  • Hot reload: Changes to your Rust code reload instantly in running apps via dynamic library swapping, providing a development experience similar to web frameworks.

WaterUI is ideal for building production mobile apps, cross-platform tools, and native desktop applications where performance and platform integration matter.

Installation

Add WaterUI to your Cargo.toml:

[dependencies]
waterui = "0.2"
waterui-ffi = "0.2"  # Required for FFI export

Enable the graphics feature for GPU rendering capabilities:

[dependencies]
waterui = { version = "0.2", features = ["graphics"] }

Quick Start

The fastest way to experience WaterUI is through the CLI's playground mode, which handles native backend setup automatically:

1. Install the CLI

cargo install --path cli

2. Create and Run a Playground

water create --playground --name my-app
cd my-app
water run

Your app launches with hot reload enabled. Edit src/lib.rs and watch changes appear instantly.

3. Write Your First View

use waterui::prelude::*;
use waterui::app::App;

#[hot_reload]
fn main() -> impl View {
    vstack((
        text("Hello, WaterUI!").size(24.0).bold(),
        text("Build native UIs with Rust"),
    ))
    .spacing(12.0)
    .padding()
}

pub fn app(env: Environment) -> App {
    App::new(main, env)
}

waterui_ffi::export!();

Core Concepts

The View Trait

Every UI component implements the View trait, which defines how it renders:

use waterui_core::Environment;

pub trait View: 'static {
    fn body(self, env: &Environment) -> impl View;
}

Views compose recursively—complex interfaces are built from simple building blocks. The framework handles type erasure, rendering, and updates automatically.

Reactive State

WaterUI uses Binding<T> for mutable state and Computed<T> for derived values. Views automatically update when reactive values change:

use waterui::prelude::*;

#[hot_reload]
fn counter() -> impl View {
    let count = Binding::int(0);

    vstack((
        waterui::text!("Count: {}", count),
        button("Increment")
            .action({
                let count = count.clone();
                move || count.set(count.get() + 1)
            }),
    ))
}

Environment

The Environment provides dependency injection for themes, fonts, and custom services. Values propagate down the view tree without explicit passing:

use waterui::app::App;
use waterui::prelude::*;
use waterui_core::Environment;
use waterui_text::font::{ResolvedFont, FontWeight};

pub fn app(mut env: Environment) -> App {
    let theme = Theme::new()
        .color_scheme(ColorScheme::Dark)
        .fonts(FontSettings::new().body(ResolvedFont::new(16.0, FontWeight::Normal)));

    env.install(theme);
    App::new(main, env)
}

View Modifiers

WaterUI provides a fluent API for styling and layout through the ViewExt trait:

use waterui::prelude::*;
use waterui_color::Color;

text("Styled Text")
    .size(18.0)
    .bold()
    .foreground(Color::srgb(100, 150, 255))
    .padding()
    .background(Color::srgb_hex("#f0f0f0"))
    .on_tap(|| println!("Tapped!"));

Examples

Form with Reactive Bindings

use waterui::prelude::*;

#[form]
struct Settings {
    username: String,
    dark_mode: bool,
    volume: f64,
}

#[hot_reload]
fn settings_view() -> impl View {
    let settings = Settings::binding();

    vstack((
        text("Settings").size(24.0),
        form(&settings),
        Divider,
        waterui::text!("Dark mode: {}", settings.project().dark_mode),
        waterui::text!("Volume: {:.0}%", settings.project().volume.map(|v| v * 100.0)),
    ))
    .padding()
}

Interactive Gesture Handling

use waterui::prelude::*;
use waterui::gesture::{TapGesture, DragGesture, LongPressGesture};

#[hot_reload]
fn gestures() -> impl View {
    let tap_count = Binding::int(0);

    vstack((
        waterui::text!("Taps: {}", tap_count),
        text("Tap Me")
            .padding()
            .background(Color::srgb_hex("#2196F3").with_opacity(0.3))
            .gesture(TapGesture::new(), {
                let tap_count = tap_count.clone();
                move || tap_count.set(tap_count.get() + 1)
            }),
        text("Long Press")
            .padding()
            .background(Color::srgb_hex("#FF9800").with_opacity(0.3))
            .gesture(LongPressGesture::new(500), || {
                println!("Long press detected");
            }),
    ))
}

Dynamic List Rendering

use waterui::prelude::*;
use waterui::component::list::{List, ListItem};

#[derive(Clone)]
struct Contact {
    id: u64,
    name: &'static str,
    role: &'static str,
}

impl Identifiable for Contact {
    type Id = u64;
    fn id(&self) -> Self::Id {
        self.id
    }
}

fn contacts_list() -> impl View {
    let contacts = vec![
        Contact { id: 1, name: "Alice Chen", role: "Software Engineer" },
        Contact { id: 2, name: "Bob Smith", role: "Product Manager" },
        Contact { id: 3, name: "Carol Williams", role: "Designer" },
    ];

    List::for_each(contacts, |contact| ListItem {
        content: AnyView::new(
            vstack((
                text(contact.name).size(17.0).bold(),
                text(contact.role)
                    .size(14.0)
                    .foreground(Color::srgb(128, 128, 128)),
            ))
            .padding_with(EdgeInsets::symmetric(12.0, 16.0)),
        ),
        on_delete: None,
    })
}

API Overview

Layout Components

  • vstack(), hstack(), zstack() - Vertical, horizontal, and depth stacks with configurable spacing and alignment
  • scroll() - Scrollable container with automatic content overflow handling
  • spacer() - Flexible space filler for pushing elements apart
  • padding() - Add insets around views with EdgeInsets configuration
  • Frame - Fixed, minimum, and maximum sizing constraints

Controls

  • Button - Tappable button with action handler and style variants
  • TextField - Single-line text input with label and placeholder
  • Toggle - Boolean switch control with reactive binding
  • Slider - Continuous value selector within a range
  • Stepper - Discrete value adjuster with step increment
  • Picker - Selection control for choosing from multiple options

Text & Media

  • Text - Styled text with font, size, weight, and color configuration
  • text!() macro - Reactive text with format string interpolation
  • styled() - Rich text with multiple style runs
  • VideoPlayer - Native video playback with controls and event handling
  • include_markdown!() - Compile-time markdown to view conversion

Form Components

  • #[form] derive macro - Automatic form generation from structs
  • TextField, Toggle, Slider - Form-compatible controls with labels
  • Automatic field-to-control mapping based on type

Navigation

  • NavigationView - Hierarchical navigation with title bar
  • TabView - Tab-based navigation container
  • .title() modifier - Set navigation bar title

Advanced

  • Dynamic::watch() - Observe reactive signals and rebuild views on change
  • AnyView - Type-erased view container for heterogeneous collections
  • Environment - Dependency injection and context propagation
  • ViewExt - Extension trait providing modifier methods for all views

Features

Default Features

The base waterui crate includes layout, controls, text, media, navigation, and form components with native rendering backends.

Optional Features

  • graphics - Enables GPU rendering with canvas drawing primitives and GpuSurface (requires waterui-graphics)
  • graphics-minimal - GPU surface only, without canvas (smaller binary size)

Application Entry Point

Every WaterUI app follows this pattern:

use waterui::prelude::*;
use waterui::app::App;

// Initialize environment (called once at app startup)
pub fn init() -> Environment {
    Environment::new()
}

// Root view (called on every render)
pub fn main() -> impl View {
    text("Your app content here")
}

// Alternative: App with custom environment setup
pub fn app(mut env: Environment) -> App {
    // Install plugins, themes, etc.
    env.install(Theme::new().color_scheme(ColorScheme::Dark));
    App::new(main, env)
}

// Export FFI entry points for native backends
waterui_ffi::export!();

The waterui_ffi::export!() macro generates C-compatible functions that native backends (Swift/Kotlin) call to render your UI.

Core Architecture

  • waterui-core - Foundation types: View trait, Environment, AnyView, reactivity primitives
  • waterui-ffi - C FFI bridge with export!() macro for native backend integration

Component Libraries

Tools

  • waterui-cli - Command-line tool for creating, building, and running apps

CLI Commands

The water CLI provides a complete development workflow:

# Create new project
water create --name "My App" --backend apple --backend android

# Create playground (auto-configured backends)
water create --playground --name my-playground

# Run with hot reload
water run --platform ios --device "iPhone 15 Pro"
water run --platform android

# Build Rust library for specific target
water build ios
water build android

# Check development environment
water doctor

# List available devices
water devices

Platform Support

  • iOS/macOS: Renders to UIKit/AppKit (requires Xcode)
  • Android: Renders to Android View (requires Android SDK)

Documentation

Contributing

Contributions are welcome! Please submit pull requests to the dev branch. The main branch is reserved for releases.

Before contributing, check:

  • Open issues for feature requests and bugs
  • ROADMAP.md for planned work
  • CLAUDE.md for development guidelines

License

Licensed under the MIT License. See LICENSE for details.