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(orANDROID_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.
zornux mobile doctor
The doctor checks the SDK, JDK, ADB, connected devices, and the project file. Fix anything it reports before continuing.
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:
zornux new mobile MyApp
cd MyApp
This creates two files:
| File | Purpose |
|---|---|
zornux.project | Project metadata — name, version, type, and Android settings. |
main.zx | The entry point with a starter app. |
The generated zornux.project looks like this:
name = MyApp
version = 0.1.0
type = mobile
entry = main.zx
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:
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 appdeclares the application — exactly one per project.screendefines a screen, like an Activity or Fragment.start withtells 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:
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:
zornux mobile build android
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:
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:
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:
| Container | Direction |
|---|---|
column … end | Vertical — children stack top to bottom. |
row … end | Horizontal — children flow left to right. |
Nest them freely to build any layout:
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:
| Widget | What it renders |
|---|---|
text expression | Displays text — a literal or any expression. |
button "label" … end | A 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:
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:
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:
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:
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:
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:
button "Save"
when tapped
store.save("draft", query)
show "Draft saved!"
end
end
Navigation summary
| Command | Effect |
|---|---|
go to ScreenName | Navigate to a screen. |
go to ScreenName with data | Navigate and pass a value. |
go back | Return to the previous screen. |
start with ScreenName | Set 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.