{{ crumbGroup }} / {{ crumbTitle }}

Introduction

Nativ is a compile-time UI compiler. You describe an app's screens once in an indentation-based .nativ language, and Nativ generates standalone, real SwiftUI and real Jetpack Compose projects — complete Xcode and Gradle output. No runtime, no bridge, no webview.

How it works

Source parses to a typed intermediate representation, then lowers to two native backends. Because both platforms share one IR, the iOS and Android apps stay in lockstep.

the pipelineoverview
.nativ source  →  parser  →  typed IR  →  SwiftUI / Xcode
                                       →  Compose / Gradle

What you get

  • Idiomatic SwiftUI in a ready-to-open .xcodeproj
  • Idiomatic Jetpack Compose in a ready-to-import Gradle project
  • Zero Nativ dependency in the output — 0 KB runtime in your binary

New here? Start with Quick start.

Installation

Nativ ships as a single binary. The compiler runs on macOS, Linux, and Windows.

Prerequisites

  • To compile: nothing but the nativ binary — nativ build only generates source files.
  • To run the iOS output: macOS with Xcode 15+ (targets iOS 17+).
  • To run the Android output: Android Studio (targets API 26+, Material 3).

Install the compiler

cargo install from crates.io is the public path today. Homebrew and the install script are next packaging targets.

$ cargo install nativ
  # Homebrew and curl installers are pending packaging.

Verify the install:

$ nativ version
nativ 0.2.0

Quick start

Create a project, write one screen, and compile it into a real Xcode project and a real Gradle project — about a minute, start to finish.

Nativ is pre-release. The language covers core screens, state, and navigation today. See honest status for what isn't generated yet.

1 · Create a project

$ nativ init my-app
 Project created: my-app/ — run `nativ build` to get started

2 · Write your first screen

Open src/screens/Home.nativ. A screen is indentation-based: declare state, lay out elements, attach actions. No imports, no boilerplate.

src/screens/Home.nativyou write
app MyApp:
  name: "my-app"
  start: Home

screen Home:
  state count = 0

  text "Hello, Nativ!", big, bold
  text "This compiles to real native code.", gray

  button "Tapped {count} times":
    count += 1

3 · Compile to native

$ nativ build
Compiling: my-app → iOS, Android
  iOS: 7 files generated
  Android: 7 files generated
✓ Build complete: 14 files, 0.01s

Open each project in its native IDE and run it like any hand-written app:

$ open build/ios/MyApp.xcodeproj
  # Android Studio → File → Open → build/android

Next: Project layout →

Project layout

nativ init scaffolds a project with a source folder, models and components directories, assets, and a config file.

my-app/tree
my-app/
├─ nativ.toml          // project configuration
├─ assets/             // images, app icon, resources
├─ i18n/
│  └─ en.json          // translation strings
└─ src/
   ├─ app.nativ        // app declaration + start screen
   ├─ components/      // reusable components
   ├─ models/          // data models
   └─ screens/
      └─ Home.nativ    // one file per screen

nativ.toml

Configuration holds app identity, target platforms, output location, theme, and i18n defaults. App identity and version flow into the generated Xcode and Gradle metadata.

nativ.tomlconfig
[app]
name = "my-app"
version = "0.1.0"
bundle_id = "com.example.myapp"

[build]
ios = true
android = true
min_ios = "17.0"
min_android = 26

[output]
directory = "build"

Images in assets/ and translation JSON in i18n/ are automatically copied into the generated Xcode asset catalogs and Android resources during the build.

UI elements

Nativ provides built-in UI elements for building interfaces. All element names are lowercase.

text

Displays static strings or dynamic values, with optional interpolation and modifiers.

nativsource
text "Hello, World!"
text user.name
text "You have {todos.count} items"
text "Welcome", big, bold

button

An interactive element that triggers an action. Buttons require a colon and an indented body.

nativsource
button "Save":
  save todo to "/todos"

button "Delete", red:
  todos.remove(todo)

Other elements

ElementPurpose
imageDisplays an image from assets or a URL — image user.avatar, round, size: 80
toggleA switch bound to a boolean — toggle todo.done
textfieldText input with binding — textfield "Name", bind: user.name
spinnerLoading indicator, no arguments
dividerHorizontal line separator
spacerFlexible empty space that pushes content apart

Layout

Layout elements control how children are arranged. A screen body is implicitly a vertical column.

column & row

column stacks children vertically; row stacks them horizontally. Both take spacing and padding.

nativsource
row spacing: 12:
  image user.avatar, round, size: 40
  column:
    text user.name, bold
    text user.email, small, gray
  spacer
  text "$9.99", bold

scroll, card & section

  • scroll — makes content scrollable when taller than the screen.
  • card — a styled container with background, shadow, and rounded corners.
  • section — groups related content with an optional header (ideal for settings).
nativsource
scroll:
  column spacing: 12:
    each item in items:
      card padding: 16:
        text item.title, bold
        text item.subtitle, gray

Modifiers & styling

Two styles: shorthand modifiers for speed and detailed modifiers for precision. Both can be combined on one element.

Shorthand

Append after an element, comma-separated. Each lowers to the platform's idiomatic call.

ModifierSwiftUICompose
big.font(.title)fontSize = 24.sp
small.font(.caption)fontSize = 12.sp
bold.bold()FontWeight.Bold
red.foregroundColor(.red)color = Color.Red
gray.foregroundColor(.secondary)onSurfaceVariant
round.clipShape(Circle())clip(CircleShape)

Detailed

Written as indented key-value pairs below an element: font, color, align, lines, size, shape, padding, corner, border, plus accessibility metadata.

nativsource
text user.name, bold
  color: #333333
  align: center

image user.avatar, round
  size: 80
  border: 2

Data & state

Keywords for defining data models, managing reactive state, fetching, and persisting.

model

Defines a data structure (PascalCase). The compiler infers types, or you declare them explicitly with defaults. ? marks an optional field.

nativsource
model Todo:
  title: text
  done: boolean = false
  created: date = now
  priority: number = 0

state

Declares reactive state scoped to a screen or component. When it changes, the UI updates automatically.

nativsource
screen Counter:
  state count: number = 0
  state items: list of Todo = []

load & persistence

  • load posts from "/posts" in an on load: block generates a real async GET (URLSession / HttpURLConnection).
  • remember count between sessions persists primitives — @AppStorage on iOS, rememberSaveable on Android.
  • A store: block selects local / secure (Keychain · EncryptedSharedPreferences) strategies.

Control flow

Conditionals, loops, and lifecycle hooks for dynamic UI.

if / else

nativsource
if count == 0:
  text "No items"
else if count == 1:
  text "1 item"
else:
  text "{count} items"

each

The only loop in Nativ — iterate a list to render elements for each item. There is no for or while; this is a UI language.

nativsource
each user in users:
  card padding: 12:
    text user.name, bold
  on tap:
    go to UserProfile(user)

Lifecycle hooks

  • on load — runs when the screen first appears (e.g. fetch data).
  • on tap — runs when an element is tapped.
  • on pull down — pull-to-refresh.
  • on close — runs as the screen disappears.

Navigation

Natural-language keywords move between screens. SwiftUI emits a NavigationStack; Compose emits Navigation Compose.

go to · go back

nativsource
button "Profile":
  go to Profile

on tap:
  go to TodoDetail(todo)

button "Cancel":
  go back

Tabs & screen parameters

Define tab navigation in the app declaration; start sets the first screen. Screens accept parameters passed via go to.

nativsource
app MyApp:
  navigation:
    tabs:
      Home, icon: house
      Search, icon: search
      Profile, icon: person
  start: Home

screen TodoDetail(todo):
  text todo.title, big
  toggle todo.done

CLI commands

The nativ binary drives the whole workflow — check, build, watch, preview, and a native dev loop.

check & build

nativ check validates .nativ files without generating code; diagnostics point at the real source line and column.

$ nativ check
No errors found

$ nativ build --ios --android
✓ Build complete: 14 files, 0.01s

watch & dev

  • nativ watch — rebuilds on every save; a parse error reports and keeps watching.
  • nativ dev --ios — builds, installs, launches, then rebuilds/relaunches the native app on each change.
  • nativ preview --open --watch — renders a static HTML mockup (one phone frame per screen).
  • nativ format — normalises spacing and indentation; --check is CI-friendly.
$ nativ watch
Build OK: 14 files generated (0.01s)
Watching . for changes... (Ctrl+C to stop)
Change detected, rebuilding...
Build OK: 14 files generated (0.01s)

Output & ownership

The output is idiomatic, hand-readable code. This screen — text, text, button with count += 1 — becomes the following.

Home.swiftSwiftUI
struct HomeView: View {
    @State private var count = 0
    var body: some View {
        VStack {
            Text("Hello, Nativ!").font(.title).bold()
            Button("Tapped \(count) times") { count += 1 }
        }
    }
}
HomeScreen.ktCompose
@Composable
fun HomeScreen() {
    var count by remember { mutableStateOf(0) }
    Column {
        Text("Hello, Nativ!", fontSize = 24.sp, fontWeight = FontWeight.Bold)
        Button(onClick = { count += 1 }) { Text("Tapped $count times") }
    }
}

What you own

The ios/ and android/ folders are ordinary native projects with no dependency on Nativ. Delete the compiler and they still build, run, and ship. The generated code is yours to edit, extend, and sell.

idiomatic SwiftUI idiomatic Compose 0 KB runtime no bridge no lock-in