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 communicates with the native UI through a versioned bridge.

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?

Implement a ViewHandler for each platform you support, register it on get_registry(), and write a small Python factory that returns Element(<type>, props, children). Step-by-step instructions live in Native views (concept).

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 speaks the same bridge protocol as the Swift and Kotlin runtimes. It shares Python components, hooks, reconciliation, and logical navigation with mobile apps; the page implements widgets in the DOM and layout with Yoga WebAssembly. 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 Yoga C++ engine compiled into each mobile runtime. Python sends styles through the bridge; native widgets supply intrinsic measurements, and the renderer returns changed frames to Python. The browser uses Yoga WebAssembly, while headless tests use the Python host binding in pythonnative.layout. The layout rules are shared, but platform fonts and controls can have different intrinsic sizes. See Layout engine.

How is state shared across screens?

Two main options:

  • use_context / Context.Provider for tree-scoped values (themes, current user). The provider sits at the top of the navigator and descendants subscribe.
  • A plain Python module-level object (a "store") for app-wide, long-lived state. Subscribe to changes via your own event bus and call set_state on a hook to trigger re-renders.

PythonNative doesn't ship a Redux-style store; the use_reducer hook covers most cases without one.

How do I navigate between screens?

Use NavigationContainer plus one of create_stack_navigator, create_tab_navigator, or create_drawer_navigator. From a screen, call use_navigation to get an imperative handle (navigate, go_back, etc.). 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