Skip to content

SDK

pythonnative.sdk is the public extension API for adding new native widgets and device modules to PythonNative. It re-exports the Element descriptor, the typed style primitives, and the native module registry so plugin authors only need a single import path. The reference here documents the symbols that are unique to the SDK module; the re-exports are documented on their canonical pages and linked below.

The full walkthrough lives in Custom native components; this page is the symbol-level reference.

Re-exports

The following names are re-exported from pythonnative.sdk for convenience and are documented on their canonical pages:

Symbol Defined in
Element Element
UNSET, UnsetType Mutation ops
Style, StyleProp, Color, Dimension, EdgeInsets, EdgeValue, FlexDirection, JustifyContent, Overflow, Position, TransformSpec, style Style
NativeModule, NativeModuleError, PythonModule, native_module, register_python_module, emit Native modules
parse_color_int Native views

Custom-component primitives

Public extension surface for PythonNative.

The pythonnative.sdk package collects the stable extension contract that third-party packages rely on: the Style type, the define_component helper that declares a custom native component and returns its typed element factory, element_factory for rebuilding that factory elsewhere, and the native module registry (native_module, register_python_module).

A custom native component is three things:

  1. A typed, frozen Props dataclass listing the public properties the component accepts.
  2. A Swift PNComponentManager and a Kotlin ComponentManager registered under the component's name by the package's native plugin (pn_plugin.json next to ios/ and android/ source folders; see docs/guides/custom-native-components.md).
  3. A define_component(name, props) call in Python that declares the element name, binds its props type, and returns the element factory.

Once defined, the component appears alongside the built-ins: the reconciler, layout engine, commit validator, contract generator, and Fast Refresh treat it identically. The browser preview draws it as a labeled placeholder; headless tests render it into the FakeBackend like any element.

A native module (device API without a view) follows the same split: a Swift / Kotlin class registered by name in the plugin, a Python facade that calls native_module(name).call(...), and optionally a Python implementation registered with register_python_module for the browser preview and tests.

PyPI packages can ship both without users importing them explicitly by declaring entry points in the pythonnative.handlers (Python side) and pythonnative.plugins (native source) groups; pn build compiles the native sources into the app.

Example
from dataclasses import dataclass
import pythonnative as pn
from pythonnative.sdk import Props, define_component


@dataclass(frozen=True)
class BadgeProps(Props):
    text: str = ""
    color: str = "#FF3B30"
    style: pn.StyleProp = None


Badge = define_component("Badge", BadgeProps)


@pn.component
def App():
    return pn.Column(
        Badge(text="3", color="#0A84FF"),
        pn.Text("Inbox"),
    )

Classes:

Name Description
Props

Optional base class for typed prop dataclasses.

Functions:

Name Description
define_component

Declare a custom native component and return its typed element factory.

element_factory

Return a callable that builds Element instances of type name.

unregister_component

Remove a previously defined component (primarily for tests).

list_components

Return the names of every defined custom component, sorted.

get_props_type

Return the props dataclass defined for name (or None).

Attributes:

Name Type Description
ENTRY_POINT_GROUP

Entry-point group used by PyPI packages to define native components.

ENTRY_POINT_GROUP module-attribute

ENTRY_POINT_GROUP = 'pythonnative.handlers'

Entry-point group used by PyPI packages to define native components.

Packages declare entries like:

[project.entry-points."pythonnative.handlers"]
my_blur = "my_pkg.blur:register"

PythonNative imports the referenced module the first time the view backend is created; the define_component calls inside that module register the package's components during import.

Props dataclass

Props()

Optional base class for typed prop dataclasses.

Subclassing is not strictly required (any @dataclass(frozen=True) works), but inheriting from Props gives third-party components a clear, searchable marker in their public API and a stable place to add framework-wide behavior in the future.

Example
from dataclasses import dataclass
from pythonnative.sdk import Props

@dataclass(frozen=True)
class BadgeProps(Props):
    text: str = ""
    color: str = "#FF3B30"

define_component

define_component(
    name: str,
    props: type,
    *,
    platforms: tuple[str, ...] = ("ios", "android"),
    measurement: str = "intrinsic"
) -> Callable[..., Element]

Declare a custom native component and return its typed element factory.

Registers props as the component's schema (so the contract generator emits it, the commit validator checks it, and the factory validates keyword arguments against it) and returns the element_factory for name. Defining the same name again replaces the earlier schema.

Parameters:

Name Type Description Default
name str

Element type name ("Badge"). Native component managers register under the same name.

required
props type

A @dataclass type describing the component's props. Every field is a keyword argument of the returned factory; style is added when the dataclass doesn't declare it.

required
platforms tuple[str, ...]

Platforms with an actual renderer. Browser placeholders don't count as native support.

('ios', 'android')
measurement str

How the renderer core sizes the view when it has no children: "intrinsic" (the default) measures the widget on the UI thread, "native" calls the manager's thread-safe static measurement from the core's worker, and "container" sizes it from its style alone.

'intrinsic'

Returns:

Type Description
Callable[..., Element]

A callable producing Element instances

Callable[..., Element]

of type name.

Raises:

Type Description
TypeError

If props is not a dataclass type.

element_factory

element_factory(name: str) -> Callable[..., Element]

Return a callable that builds Element instances of type name.

The returned factory accepts:

  • Children as positional arguments (any number).
  • key= (optional, keyword-only) for keyed reconciliation.
  • Either props= (a dataclass instance) or per-field keyword arguments matching the defined props dataclass.

Parameters:

Name Type Description Default
name str

An element type name previously declared with define_component.

required

Returns:

Type Description
Callable[..., Element]

A callable producing fresh

Callable[..., Element]

Element instances of type name.

Raises:

Type Description
KeyError

If name is not defined.

Example
Badge = element_factory("Badge")
Badge(text="3", color="#0A84FF")
Badge(props=BadgeProps(text="3"))

unregister_component

unregister_component(name: str) -> None

Remove a previously defined component (primarily for tests).

Parameters:

Name Type Description Default
name str

The element type name to forget.

required

list_components

list_components() -> List[str]

Return the names of every defined custom component, sorted.

get_props_type

get_props_type(name: str) -> Optional[type]

Return the props dataclass defined for name (or None).

Contract schemas

define_component registers the props dataclass as a ComponentSchema; native modules declare a ModuleSchema from a Protocol. Field-level options such as layout invalidation and platform support are declared with NativeField. pn codegen compiles the registered schemas into Swift and Kotlin; see Generated native contracts.

Python definitions for native props, events, commands, and modules.

Classes:

Name Description
ComponentSchema

The portable contract shared by every renderer and the component SDK.

ModuleSchema

Typed native module methods, arguments, results, and async behavior.

NativeField

Native behavior attached with Annotated[T, NativeField(...)].

Functions:

Name Description
register_schema

Register the contract used to generate and validate a native extension.

ComponentSchema dataclass

ComponentSchema(
    name: str,
    props: dict[str, dict[str, Any]],
    required: tuple[str, ...] = (),
    defaults: dict[str, Any] = dict(),
    measurement: str = "intrinsic",
    commands: dict[str, Any] = dict(),
    platforms: tuple[str, ...] = ("ios", "android", "web"),
    flatten: bool = False,
    adopt_flattened: bool = False,
    detach_children: bool = False,
    clip: bool = False,
    portal: bool = False,
)

The portable contract shared by every renderer and the component SDK.

Methods:

Name Description
from_dataclass

Describe a native widget with a frozen Python props dataclass.

validate

Validate a construction or a partial update against this contract.

validate_event

Validate a typed callback payload before application code receives it.

decode_event

Reconstruct declared payload records after validating an event.

Attributes:

Name Type Description
measurement str

How the renderer core sizes the component as a leaf: "container"

flatten bool

Whether the core may leave the view unmounted when it only affects layout.

adopt_flattened bool

Whether the view may receive the children of a flattened child.

detach_children bool

Whether children lay out as independent roots (lists, modals, stacks).

clip bool

Whether layout clips overflow and lets the view shrink by default (scrolling containers).

portal bool

Whether the view lays out against its own container instead of its parent.

measurement class-attribute instance-attribute

measurement: str = 'intrinsic'

How the renderer core sizes the component as a leaf: "container" (never measured), "intrinsic" (a widget measured on the UI thread), "native" (a plugin's thread-safe measurement), or one the core computes for built-ins ("text", "image", "svg", "fill").

flatten class-attribute instance-attribute

flatten: bool = False

Whether the core may leave the view unmounted when it only affects layout.

adopt_flattened class-attribute instance-attribute

adopt_flattened: bool = False

Whether the view may receive the children of a flattened child.

detach_children class-attribute instance-attribute

detach_children: bool = False

Whether children lay out as independent roots (lists, modals, stacks).

clip class-attribute instance-attribute

clip: bool = False

Whether layout clips overflow and lets the view shrink by default (scrolling containers).

portal class-attribute instance-attribute

portal: bool = False

Whether the view lays out against its own container instead of its parent.

from_dataclass classmethod

from_dataclass(
    name: str,
    props: type,
    *,
    measurement: str = "intrinsic",
    platforms: tuple[str, ...] = ("ios", "android")
) -> ComponentSchema

Describe a native widget with a frozen Python props dataclass.

validate

validate(
    props: Mapping[str, Any],
    *,
    partial: bool = False,
    platform: str | None = None
) -> None

Validate a construction or a partial update against this contract.

validate_event

validate_event(name: str, arguments: list[Any]) -> None

Validate a typed callback payload before application code receives it.

decode_event

decode_event(name: str, arguments: list[Any]) -> list[Any]

Reconstruct declared payload records after validating an event.

ModuleSchema dataclass

ModuleSchema(
    name: str,
    methods: dict[str, dict[str, Any]],
    events: dict[str, dict[str, Any]] = dict(),
)

Typed native module methods, arguments, results, and async behavior.

Methods:

Name Description
from_protocol

Read annotated methods from a Python protocol or interface class.

validate_call

Validate and normalize a method invocation before crossing a bridge.

decode_event

Validate and restore the declared Python payload of a module event.

validate_result

Reject a native result that doesn't satisfy its declared return type.

from_protocol classmethod

from_protocol(
    name: str,
    protocol: type,
    *,
    events: Mapping[str, Any] | None = None
) -> ModuleSchema

Read annotated methods from a Python protocol or interface class.

validate_call

validate_call(
    method: str, arguments: Mapping[str, Any]
) -> dict[str, Any]

Validate and normalize a method invocation before crossing a bridge.

decode_event

decode_event(event: str, payload: Any) -> Any

Validate and restore the declared Python payload of a module event.

validate_result

validate_result(method: str, result: Any) -> Any

Reject a native result that doesn't satisfy its declared return type.

NativeField dataclass

NativeField(
    invalidates_layout: bool = False,
    recreate: bool = False,
    animated: bool = False,
    platforms: tuple[str, ...] = ("ios", "android", "web"),
    description: str = "",
    python_only: bool = False,
    flatten: bool = False,
)

Native behavior attached with Annotated[T, NativeField(...)].

Attributes:

Name Type Description
flatten bool

A layout-only prop: setting it keeps a view eligible for flattening.

flatten class-attribute instance-attribute

flatten: bool = False

A layout-only prop: setting it keeps a view eligible for flattening.

register_schema

register_schema(
    schema: ComponentSchema | ModuleSchema,
) -> None

Register the contract used to generate and validate a native extension.

Entry-point discovery

Third-party packages can define components automatically by exposing an entry point in the pythonnative.handlers group (the value of ENTRY_POINT_GROUP). The view backend loads every registered entry point exactly once, before the first native commit is validated. A misbehaving plugin raises an exception that is caught and logged; it never breaks PythonNative startup.

# In your plugin's pyproject.toml
[project.entry-points."pythonnative.handlers"]
my_widget = "my_pkg:register"

The module pointed at by the entry point should perform whatever imports are needed to run its define_component calls.

Next steps