Skip to content

FAQ

Conceptual questions about PythonNative. For specific error messages and their fixes, see Troubleshooting.

Why "PythonNative" and not, say, Toga or Kivy?

PythonNative renders real native widgets through Swift and Kotlin component managers. A pn.Text becomes a UILabel / TextView; a pn.Button becomes a UIButton / Button. Platform controls provide native behavior. Applications still need appropriate accessibility labels, roles, and testing on each deployment target.

The component model is React-style (functions plus hooks plus a reconciler) so you get a familiar declarative API on top of those native widgets. Python runs on its own application thread and commits through a versioned bridge to a shared C++ renderer core, which validates and lays out each commit before the platform's UI thread mounts it.

Can I write Android-only or iOS-only code?

Yes. Two patterns:

  • Branch on IS_ANDROID / IS_IOS / IS_WEB for small differences inside a shared component:

    from pythonnative.utils import IS_ANDROID, IS_IOS, IS_WEB
    
    if IS_ANDROID:
        ...
    elif IS_IOS:
        ...
    elif IS_WEB:
        ...
    

    pn preview and pn start set PN_PLATFORM=web, so IS_WEB is True in the browser preview; see the Browser preview guide. For a declarative alternative, use Platform.select with the "web" key.

  • Per-platform native modules for larger pieces (a custom widget, a device API). Implement once per platform behind a single Python facade. See Native modules guide.

How do I add a new widget?

Declare a frozen Props dataclass, call define_component(name, props) to get a typed element factory, generate the native contract, and implement a Swift PNComponentManager and a Kotlin ComponentManager under the same name. Headless tests and the browser preview need no extra Python code. Step-by-step instructions live in Custom native components.

Does PythonNative work on the desktop or the web?

For previewing, yes. pn preview renders your app in a browser tab inside a phone frame, with Fast Refresh on every save. It's the fastest inner loop: see your real UI and iterate in seconds without booting a simulator or deploying to a device. The page runs the same renderer core as the mobile apps, compiled to WebAssembly, and shares Python components, hooks, reconciliation, and logical navigation with them; it implements widgets in the DOM. See the Browser preview guide.

The browser preview is a development tool, not a production target: platform chrome and fonts are approximated, most device APIs are simulated, and there's no web or desktop packaging. Ship to devices with pn run android / pn run ios and pn build.

The core (components, hooks, reconciler) is also platform-agnostic and runs headless with a fake backend; that's how the unit-test suite works.

How do I package and distribute my app?

pn run android and pn run ios produce installable artifacts in build/:

  • Android: build/android/android_template/app/build/outputs/apk/.
  • iOS: build/ios/ios_template/build/Build/Products/Debug-iphonesimulator/ios_template.app.

For release distribution, treat the staged template directory as a normal Android Studio / Xcode project (sign, archive, upload via Play Console / App Store Connect).

Can I use any Python package?

Most of them. List packages in [requirements].packages in pythonnative.toml and the pn CLI resolves them for the phone, not for your laptop, then bundles them into the app:

  • Pure-Python packages (requests, httpx, pydantic-core-free libraries, and so on) work everywhere.
  • Binary wheels work when the package publishes wheels for the target: iOS wheels (PEP 730, ios_13_0_arm64_iphoneos and the Simulator tags) from the BeeWare index, and Android wheels from the Chaquopy index. numpy, Pillow, cryptography, cffi, lxml, and pandas all resolve out of the box today.
  • No wheel for the target is a build-time error, reported by pn deps with the package name and the target that failed, never a silent import error on the device.

Run pn deps to see how every requirement resolves for each iOS and Android target before you build. The PyPI packages guide covers the details and the live compatibility matrix.

Don't put pythonnative itself in [requirements].packages; the CLI bundles the installed copy directly.

Where do flex, padding, and position: "absolute" actually run?

In the renderer core, a C++ library with Yoga built in that every target links: the iOS and Android apps, the browser preview (as WebAssembly), and the pythonnative._core extension headless tests use. Python sends styles in each commit; the core lays out off the UI thread, asks the platform to measure text and controls, and returns changed frames to Python. The layout rules are shared, but platform fonts and controls can have different intrinsic sizes. See Layout engine.

How is state shared across screens?

Every screen is part of one component tree, so the usual tools work across screens:

  • A pn.Store for app-wide, long-lived state. Define it at module level, change it with plain functions that call store.update(...), and read it with use_store. A selector (pn.use_store(store, lambda s: s.unread)) re-renders a component only when the value it reads changes. It fills the role of Zustand or Redux in a React Native app.
  • use_context / Context.Provider for tree-scoped values (the current user, a repository object). Place the provider above the NavigationContainer so every screen can read it.
  • Screen params for the data a screen needs to show: pass an id as a parameter (nav.push(ItemScreen(id=42))) and look the record up in the screen.

See Managing state for when to use each, and for persisting state across launches.

How do I navigate between screens?

Define your screens as components, list them in a module-level StackNavigator, TabNavigator, or DrawerNavigator, and render it with NavigationContainer. From a screen, call use_navigation and pass the destination as a call to its component: nav.push(ItemScreen(id=42)). The screen's parameters are its route params, so the type checker verifies them. See the Navigation guide.

Why hooks instead of class components?

Hooks are smaller (no inheritance), composable (a custom hook is just a Python function that calls other hooks), and produce simpler diffs. The React community's experience over the last few years made the choice straightforward.

Why function components and not just plain functions?

The @pn.component decorator establishes a hook context for the function call. Without it, hooks have no slot to attach state to and will raise. The decorator also gives the reconciler a stable identity for the function so it can keep state across re-renders.

How do I do animations?

Two strategies, depending on the cadence:

  • State-driven (e.g., a fade triggered by a button press): keep the value in use_state and let the reconciler push updates. Fine up to a few updates per second.
  • Frame-driven (e.g., a spring animation): bind an AnimatedValue into an Animated.View style and drive it with Animated.spring or Animated.timing. Native animation graphs update supported bindings without running Python on every frame. See the Animations guide for examples and fallback cases.

Is there async/await support?

Yes, and it's the core of the framework. PythonNative runs a single standard asyncio loop on a dedicated application thread. Native UI threads own widgets, scrolling, and animation frames. Components themselves can be async def (pair them with Suspense for loading states), use_effect accepts coroutine callbacks and cancels them on unmount, use_resource fetches during render, and native modules like Camera and Location are awaitable. From a synchronous event handler, kick off work with pn.run_async. See the Async + data guide.

How is this different from Toga, BeeWare, Kivy, Briefcase?

Tool Model Widgets
PythonNative Declarative components plus reconciler Real native (UILabel, TextView) via Swift / Kotlin component managers
Toga Imperative widgets Real native via per-platform backend
Kivy Imperative widgets Custom OpenGL renderer
Briefcase Packaging only (no widgets) n/a

PythonNative also has a built-in pn CLI for the build / install / hot-reload loop, so the equivalent of "Briefcase" is included.

Where do I file bugs and feature requests?

GitHub issues. Smaller fixes and docs improvements are welcome as pull requests without a prior issue. See Contributing.

Next steps