PythonNative combines direct native bindings with a declarative reconciler, giving you React-like ergonomics while calling native platform APIs synchronously from Python.
- Declarative element tree: Your
Page.render()method returns a tree ofElementdescriptors (similar to React elements / virtual DOM nodes). - Function components and hooks: Reusable components with independent state via
@pn.component,use_state,use_effect, etc. — inspired by React hooks but designed for Python. - Reconciler: On first render, the reconciler walks the tree and creates real native views via the platform backend. On subsequent renders (triggered by
set_stateor hook state changes), it diffs the new tree against the old one and applies the minimal set of native mutations. - Key-based reconciliation: Children can be assigned stable
keyvalues to preserve identity across re-renders — critical for lists and dynamic content. - Direct bindings: Under the hood, native views are created and updated through direct platform calls:
- iOS: rubicon-objc exposes Objective-C/Swift classes (
UILabel,UIButton,UIStackView, etc.). - Android: Chaquopy exposes Java classes (
android.widget.TextView,android.widget.Button, etc.) via the JNI bridge.
- iOS: rubicon-objc exposes Objective-C/Swift classes (
- Thin native bootstrap: The host app remains native (Android
Activityor iOSUIViewController). It passes a live instance/pointer into Python, and Python drives the UI through the reconciler.
Page.render() → Element tree → Reconciler → Native views
↑
Page.set_state() → re-render → diff → patch native views
Hook set_state() → re-render → diff → patch native views
The reconciler uses key-based diffing (matching children by key first, then by position). When a child with the same key/type is found, its props are updated in-place on the native view. When the type changes, the old native view is destroyed and a new one is created.
PythonNative supports two kinds of components:
Each screen is a Page subclass that bridges native lifecycle events to Python. Pages have render(), set_state(), navigation (push/pop), and lifecycle hooks (on_create, on_resume, etc.).
Decorated with @pn.component, these are Python functions that return Element trees and can use hooks for state, effects, memoisation, and context. Each call site creates an independent instance with its own hook state.
@pn.component
def counter(initial: int = 0) -> pn.Element:
count, set_count = pn.use_state(initial)
return pn.Text(f"Count: {count}")- Inline styles: Pass props directly to components (
font_size=24,color="#333"). - StyleSheet: Create reusable named style dictionaries with
pn.StyleSheet.create(...). - Theming: Use
pn.ThemeContextwithpn.Providerandpn.use_contextto propagate theme values through the tree.
All components support layout properties: width, height, flex, margin, min_width, max_width, min_height, max_height, align_self. Containers (Column, Row) support spacing, padding, and alignment.
- Versus React Native: RN uses JSX + a JavaScript bridge + Yoga layout. PythonNative uses Python + direct native calls + platform layout managers. No JS bridge, no serialisation overhead.
- Versus NativeScript: Similar philosophy (direct, synchronous native access), but PythonNative adds a declarative reconciler layer and React-like hooks that NativeScript does not have by default.
- The iOS template (Swift + PythonKit) boots Python and instantiates your
MainPagewith the currentUIViewControllerpointer. Page.on_create()callsrender(), the reconciler creates UIKit views, and attaches them to the controller's view.- State changes trigger
render()again; the reconciler patches UIKit views in-place.
- The Android template (Kotlin + Chaquopy) initializes Python in
MainActivityand passes theActivityto Python. PageFragmentcallson_create()on the PythonPage, which renders and attaches views to the fragment container.- State changes trigger re-render; the reconciler patches Android views in-place.
During development, pn run --hot-reload watches app/ for file changes and pushes updated Python files to the running app, enabling near-instant UI updates without full rebuilds.
PythonNative provides cross-platform modules for common device APIs:
pythonnative.native_modules.Camera— photo capture and gallerypythonnative.native_modules.Location— GPS / location servicespythonnative.native_modules.FileSystem— app-scoped file I/Opythonnative.native_modules.Notifications— local push notifications
- See the Navigation guide for full details.
- iOS: one host
UIViewControllerclass, many instances pushed on aUINavigationController. - Android: single host
Activitywith aNavHostFragmentand a stack of genericPageFragments driven by a navigation graph.
- iOS: one host
- Guides / Android: guides/android.md
- Guides / iOS: guides/ios.md
- Concepts / Components: concepts/components.md