Skip to content

PythonNative

PythonNative is a cross-platform toolkit for building native Android and iOS apps in plain Python. The component model is React-style (function components plus hooks plus a reconciler); rendering and device APIs are native Swift and Kotlin, driven over a small bridge with one transaction per commit. Application components run in Python.

A taste

import pythonnative as pn


@pn.component
def Counter(initial: int = 0) -> pn.Node:
    count, set_count = pn.use_state(initial)
    return pn.Column(
        pn.Text(f"Count: {count}", style=pn.style(font_size=24, bold=True)),
        pn.Button("+", on_press=lambda: set_count(count + 1)),
        style=pn.style(gap=12, padding=16),
    )

That same Counter mounts as a UILabel plus a UIButton inside a UIView on iOS, and as a TextView plus a Button inside a FrameLayout on Android. A shared C++ renderer core interprets flex, padding, and position with Yoga, off the UI thread. Platform controls and fonts supply their own intrinsic sizes.

Why PythonNative?

  • Real native widgets. UIKit and Android controls provide platform behavior. Configure accessibility labels and roles, and test navigation and interaction on each platform.
  • A familiar component model. If you know React or React Native, you already know how PythonNative works.
  • Python application code. Components run on a dedicated asyncio application thread. Validated commits connect Python state to native widgets.
  • Ordinary asyncio. One standard application loop runs Python work independently of the native UI thread. Components can be async def and await data right in the body, with Suspense providing the loading state declaratively. See the Async + data guide.
  • Typed from end to end. A component's signature is its prop list and it returns a pn.Node, so a strict type checker verifies element trees, props, and conditional children. pn.Style is a TypedDict with Literal enums for every fixed-value field, so mypy and your editor catch typos in align_items or font_weight before the app ever runs.
  • Screens are components. A screen's parameters are its route params, so nav.push(ItemScreen(id=42)) is a checked call. Navigators are module-level values, and deep links come from each screen's path. See Navigation.
  • Native-backed navigation. Every stack drives the platform's real navigation controller (fragments on Android, UINavigationController on iOS), so transitions, back gestures, and state preservation are exactly what users expect from a first-class native app.
  • One theme, typed style sheets, and stores. A Theme dataclass styles the app and its navigators and follows dark mode; StyleSheet namespaces give styles typed names; and a Store holds app state with targeted re-renders. See Styling and Managing state.
  • A Metro-style dev loop with PythonNative Go. pn start runs one dev server for the browser preview and every connected app, and prints a QR code. PythonNative Go, a prebuilt dev client, runs any project without native code on a simulator, emulator, or phone without compiling the project. Save a file and each client Fast Refreshes in place, preserving component state. See the Development workflow and PythonNative Go and DevTools.
  • DevTools and breakpoints. A DevTools page shows the component tree with props and hooks, a REPL inside the running app, problems, network requests, and performance. VS Code attaches debugpy breakpoints to the Python running on a phone. See Debugging.
  • Dev-mode diagnostics. Uncaught errors show a full-screen error screen with the component stack and your source instead of crashing; typos in style keys and duplicate list keys print "did you mean" warnings; props that don't match a component's annotations warn; conditional hooks raise at the source. Every check is skipped in production, and pn lint catches hook mistakes before you run the app.
  • Browser preview. pn preview renders your app in a browser tab inside a phone frame, through the same bridge protocol the Swift and Kotlin runtimes speak, so you can iterate on UI, state, and navigation in milliseconds (no simulator boot required). See the Browser preview guide.
  • Device features you opt into. Video, audio, camera, maps, location, notifications, push, pickers, sensors, biometrics, background tasks, and in-app purchases are capabilities: list one in [plugins].use and only then does it compile into your app, so an app never references a privacy-sensitive API it doesn't call.
  • An extension SDK. pythonnative.sdk lets you wrap any platform widget as a first-class element with type-checked props through define_component and generates the Swift and Kotlin contract from the same declaration. Native plugins use the same format as the first-party capabilities, and a package in your requirements that ships one is compiled in automatically.
  • A path to the stores. pn build refuses a release a store would reject, pn run --release runs that configuration on a simulator or emulator, and pn submit uploads it. CI proves the artifacts and the request flow, but no real store submission has been verified yet. See Building for release.
  • Production failure handling. pn.set_error_handler reports every uncaught error, and a release build replaces a broken screen with a fallback instead of leaving it stuck. See Error boundaries.
  • A small surface. A handful of element factories, a handful of hooks, and three navigators.

Project status

PythonNative is under active development. The public API documented on this site is the supported surface; expect breaking changes only at minor version bumps until 1.0. See the Changelog for what shipped in each release.

Get involved

Next steps