PythonNative uses a declarative component model inspired by React. You describe what the UI should look like, and the framework handles creating and updating native views.
UI is built with element-creating functions. Each returns a lightweight Element descriptor — no native objects are created until the reconciler mounts the tree.
import pythonnative as pn
pn.Text("Hello", style={"font_size": 18, "color": "#333333"})
pn.Button("Tap me", on_click=lambda: print("tapped"))
pn.Column(
pn.Text("First"),
pn.Text("Second"),
style={"spacing": 8, "padding": 16},
)Layout:
Column(*children, style=...)— vertical stackRow(*children, style=...)— horizontal stackScrollView(child, style=...)— scrollable containerView(*children, style=...)— generic containerSafeAreaView(*children, style=...)— safe-area-aware containerSpacer(size, flex)— empty space
Display:
Text(text, style=...)— text displayImage(source, style=...)— image display (supports URLs and resource names)WebView(url)— embedded web content
Input:
Button(title, on_click, style=...)— tappable buttonTextInput(value, placeholder, on_change, secure, style=...)— text entrySwitch(value, on_change)— toggle switchSlider(value, min_value, max_value, on_change)— continuous sliderPressable(child, on_press, on_long_press)— tap handler wrapper
Feedback:
ProgressBar(value)— determinate progress (0.0–1.0)ActivityIndicator(animating)— indeterminate spinner
Overlay:
Modal(*children, visible, on_dismiss, title)— modal dialog
Lists:
FlatList(data, render_item, key_extractor, separator_height)— scrollable data list
All components accept layout properties inside the style dict:
width,height— fixed dimensions (dp / pt)flex— flex grow factormargin— outer margin (int, float, or dict like padding)min_width,max_width,min_height,max_height— size constraintsalign_self— override parent alignment for this child
All UI in PythonNative is built with @pn.component function components. Each screen is a function component that returns an element tree:
@pn.component
def MainPage():
name, set_name = pn.use_state("World")
return pn.Text(f"Hello, {name}!", style={"font_size": 24})The entry point create_page() is called internally by native templates to bootstrap your root component. You don't call it directly — just export your component and configure the entry point in pythonnative.json.
Use pn.use_state(initial) to create local component state. Call the setter to update — the framework automatically re-renders the component and applies only the differences to the native views:
@pn.component
def CounterPage():
count, set_count = pn.use_state(0)
return pn.Column(
pn.Text(f"Count: {count}", style={"font_size": 24}),
pn.Button("Increment", on_click=lambda: set_count(count + 1)),
style={"spacing": 12},
)Build complex UIs by composing smaller @pn.component functions. Each instance has independent state:
@pn.component
def Counter(label: str = "Count", initial: int = 0):
count, set_count = pn.use_state(initial)
return pn.Column(
pn.Text(f"{label}: {count}", style={"font_size": 18}),
pn.Row(
pn.Button("-", on_click=lambda: set_count(count - 1)),
pn.Button("+", on_click=lambda: set_count(count + 1)),
style={"spacing": 8},
),
style={"spacing": 4},
)
@pn.component
def MainPage():
return pn.Column(
Counter(label="Apples", initial=0),
Counter(label="Oranges", initial=5),
style={"spacing": 16, "padding": 16},
)Changing one Counter doesn't affect the other — each has its own hook state.
use_state(initial)— local component state; returns(value, setter)use_effect(effect, deps)— side effects (timers, API calls, subscriptions)use_memo(factory, deps)— memoised computed valuesuse_callback(fn, deps)— stable function referencesuse_ref(initial)— mutable ref that persists across rendersuse_context(context)— read from a context provideruse_navigation()— navigation handle for push/pop between screens
Extract reusable stateful logic into plain functions:
def use_toggle(initial: bool = False):
value, set_value = pn.use_state(initial)
def toggle():
set_value(not value)
return value, toggleShare values across the tree without prop drilling:
theme = pn.create_context({"primary": "#007AFF"})
@pn.component
def App():
return pn.Provider(theme, {"primary": "#FF0000"},
MyComponent()
)
@pn.component
def MyComponent():
t = pn.use_context(theme)
return pn.Button("Click", style={"color": t["primary"]})Use pythonnative.utils.IS_ANDROID when you need platform-specific logic:
from pythonnative.utils import IS_ANDROID
title = "Android App" if IS_ANDROID else "iOS App"