Skip to content

Components

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.

Element functions

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},
)

Available components

Layout:

Display:

Input:

Feedback:

Overlay:

Error handling:

Composition:

  • Fragment(*children): group siblings into a parent's child list without an extra wrapping view (analogous to React's <>…</>).

Lists:

Platform UI:

Imperative APIs:

Animations:

  • Animated.View / Animated.Text / Animated.Image: components whose style accepts AnimatedValue instances. Drive animations with Animated.timing, Animated.spring, or Animated.decay. See the Animations guide.

Flex layout model

PythonNative uses a flexbox-inspired layout model. View is the universal flex container; Column and Row are convenience wrappers that fix the direction.

Flex container properties (inside style)

  • flex_direction: "column" (default), "row", "column_reverse", "row_reverse".
  • justify_content: main-axis distribution: "flex_start", "center", "flex_end", "space_between", "space_around", "space_evenly".
  • align_items: cross-axis alignment: "stretch", "flex_start", "center", "flex_end".
  • overflow: "visible" (default), "hidden".
  • spacing: gap between children (dp / pt).
  • padding: inner spacing.

Child layout properties

All components accept these in their style dict:

  • width, height: fixed dimensions (dp / pt).
  • flex: flex grow factor (shorthand).
  • flex_grow, flex_shrink: individual flex properties.
  • margin: outer margin (int, float, or dict like padding).
  • min_width, min_height: minimum size constraints.
  • max_width, max_height: maximum size constraints.
  • align_self: override parent alignment for this child.

Example: centering content

pn.View(
    pn.Text("Centered"),
    style={"flex": 1, "justify_content": "center", "align_items": "center"},
)

Example: horizontal row with spacing

pn.Row(
    pn.Button("Cancel"),
    pn.Spacer(flex=1),
    pn.Button("OK"),
    style={"padding": 16, "align_items": "center"},
)

Function components: the building block

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 App():
    name, set_name = pn.use_state("World")
    return pn.Text(f"Hello, {name}!", style={"font_size": 24})

The entry point create_screen is called internally by native templates to bootstrap your root component. You don't call it directly: name your top-level component App (so the templates can find it by convention) and pythonnative.json points at the module that defines it.

State and re-rendering

Use 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},
    )

Composing components

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 App():
    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.

Available hooks

Custom hooks

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, toggle

Context and Provider

Share 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"]})

Platform detection

The recommended way to write platform-aware code is via Platform:

import pythonnative as pn

title = pn.Platform.select({"ios": "iOS App", "android": "Android App"})

if pn.Platform.is_ios:
    margin = 16

pn.Platform.OS is "ios", "android", or "test" (the latter when running off-device, e.g., in unit tests). The lower-level utils.IS_ANDROID / utils.IS_IOS constants are still available.

Next steps