Zornux docs
Get started Spec

Mobile

Mobile Development

Zornux compiles to native Android apps — the same human-readable language you already know, targeting Kotlin and Jetpack Compose with no intermediate framework.

Prerequisites

Before you begin, make sure the following are installed. Run zornux mobile doctor to check everything at once:

  • Zornux 1.8.0+ — the mobile toolchain ships with the standard install.
  • Android SDK — set ANDROID_HOME (or ANDROID_SDK_ROOT) to your SDK path.
  • JDK 17 or later — set JAVA_HOME. JDK 16 and below are refused with a clear diagnostic.
  • An Android device or emulator — USB debugging enabled, or an AVD configured.
bash
zornux mobile doctor

The doctor checks the SDK, JDK, ADB, connected devices, and the project file. Fix anything it reports before continuing.

Gradle is handled for you

Zornux generates a Gradle wrapper pinned to Gradle 8.9 inside every mobile project — you do not need to install Gradle yourself.

Create a project

Scaffold a new mobile project with zornux new mobile:

bash
zornux new mobile MyApp
cd MyApp

This creates two files:

FilePurpose
zornux.projectProject metadata — name, version, type, and Android settings.
main.zxThe entry point with a starter app.

The generated zornux.project looks like this:

zxcfg
name = MyApp
version = 0.1.0
type = mobile
entry = main.zx
The type field

A mobile project must set type = mobile. This enables the mobile syntax, code generation, and all zornux mobile subcommands.

Your first mobile app

The generated main.zx is a complete runnable app:

zornux
mobile app "MyApp"

screen Main
    column
        text "Welcome to MyApp!"
        button "Tap me"
            when tapped
                show "Hello!"
            end
        end
    end
end

start with Main

Three things to notice:

  • mobile app declares the application — exactly one per project.
  • screen defines a screen, like an Activity or Fragment.
  • start with tells Zornux which screen to show first.

Build and run

Build the project, install it on a connected device or running emulator, and launch it — all in one command:

bash
zornux mobile run android

Under the hood, Zornux generates a complete Kotlin/Compose Android project, runs Gradle, and installs the APK. You can also build without installing:

bash
zornux mobile build android
Target a specific device

If multiple devices are connected, pass --device <serial> to choose one. Run zornux mobile devices to list them.

Screens

Screens are the building blocks of navigation. Each screen is a self-contained view with its own layout and logic:

zornux
screen Home
    column
        text "Home screen"
        button "Go to settings"
            when tapped
                go to Settings
            end
        end
    end
end

screen Settings
    column
        text "Settings"
        button "Go back"
            when tapped
                go back
            end
        end
    end
end

start with Home

Passing data between screens

A screen can receive a parameter. Pass it with go to … with:

zornux
screen UserList
    column
        button "View Alice"
            when tapped
                go to Profile with "Alice"
            end
        end
    end
end

screen Profile receives userName
    column
        text "Profile: " + userName
    end
end

Layouts

Two layout containers organize widgets on screen:

ContainerDirection
column … endVertical — children stack top to bottom.
row … endHorizontal — children flow left to right.

Nest them freely to build any layout:

zornux
screen Dashboard
    column
        text "Dashboard"
        row
            button "Profile"
                when tapped
                    go to Profile
                end
            end
            button "Settings"
                when tapped
                    go to Settings
                end
            end
        end
    end
end

Widgets

Widgets are the visible elements inside a layout. Every widget lives inside a column or row:

WidgetWhat it renders
text expressionDisplays text — a literal or any expression.
button "label" … endA tappable button with a when tapped handler.
input stateName "label"A text field bound to a state variable.

State

Screens can hold reactive state. Declare state variables at the top of a screen — when a state changes, the screen re-renders:

zornux
screen Counter
    state count = 0

    column
        text "Count: " + count
        button "Increment"
            when tapped
                count = count + 1
            end
        end
    end
end

Bind a text input to state with the input widget. The value updates as the user types:

zornux
screen Search
    state query = ""

    column
        input query "Search..."
        text "You typed: " + query
    end
end

Conditional rendering

Use if / else if / else inside a layout to show or hide widgets based on state:

zornux
screen Login
    state loggedIn = false

    column
        if loggedIn
            text "Welcome back!"
            button "Log out"
                when tapped
                    loggedIn = false
                end
            end
        else
            text "Please log in"
            button "Log in"
                when tapped
                    loggedIn = true
                end
            end
        end
    end
end

Lists

Render a collection with for each. This generates a scrollable list:

zornux
screen TodoList
    state todos = ["Buy groceries", "Walk the dog", "Write code"]

    column
        text "My Todos"
        for each todo in todos
            text todo
        end
    end
end

Lifecycle

Run code when a screen first appears with when screen opens:

zornux
screen Feed
    state posts = []

    when screen opens
        posts = http.get("https://api.example.com/posts")
    end

    column
        text "Latest Posts"
        for each post in posts
            text post
        end
    end
end

Toasts and alerts

The show keyword displays a toast message:

zornux
button "Save"
    when tapped
        store.save("draft", query)
        show "Draft saved!"
    end
end

Navigation summary

CommandEffect
go to ScreenNameNavigate to a screen.
go to ScreenName with dataNavigate and pass a value.
go backReturn to the previous screen.
start with ScreenNameSet the app's entry screen (top-level only).

What's next

The language syntax covers screens, layouts, and navigation. From here:

  • Mobile Capabilities — camera, location, notifications, secure storage, biometrics, HTTP, permissions, and native extensions.
  • Mobile Tooling — the full CLI: hot reload, debugging, testing, profiling, release builds, and Play Store publishing.