Skip to content

Python API

Elements and components

@component wraps a Python function. Its arguments are props. h(Component, *children, **props) or Component(*children, **props) creates an element; it does not call the function eagerly. Python components receive original Python objects as props. Treat props and state as immutable snapshots.

html.div, html.button, svg.circle, or html("my-element", ...) cover all DOM tag names. Child values may be elements, text, numbers, booleans/None, lists/tuples or generators. A bare Python dict is not a valid child.

DOM prop conventions:

Python React/browser
class_, class_name className
html_for, for_ htmlFor
on_click, on_key_down onClick, onKeyDown
aria_label, data_testid aria-label, data-testid
style={"background_color": "red"} style={{backgroundColor: "red"}}

Custom Python prop names are preserved, not automatically camel-cased.

fragment, strict_mode, suspense, activity, view_transition, profiler adapt the corresponding built-in React components. memo(Component, compare=None), lazy(loader), forward_ref, create_context(default), Context.provide(*children, value=...), create_portal are also available.

error_boundary(*children, fallback=..., reset_key=...) isolates render errors. Its fallback may be an element or a callback receiving the JS error. Changing reset_key resets it. Event-handler errors are not render errors and are not caught by a React error boundary.

is_valid_element, clone_element, children_count, children_to_array, map_children expose common element utilities. map_children callbacks receive (child, index).

Hooks

Python React
use_state(initial) useState
use_reducer(reducer, initial, initializer=None) useReducer
use_context(context) useContext
use_ref(initial=None) useRef
use_effect(effect, dependencies=None) useEffect
use_layout_effect(effect, dependencies=None) useLayoutEffect
use_insertion_effect(effect, dependencies=None) useInsertionEffect
use_memo(factory, dependencies) useMemo
use_callback(fn, dependencies) useCallback
use_id() useId
use_imperative_handle(ref, factory, dependencies=None) useImperativeHandle
use_sync_external_store(subscribe, get_snapshot, get_server_snapshot=None) useSyncExternalStore
use_transition() useTransition
use_deferred_value(value, initial=...) useDeferredValue
use_optimistic(value, reducer=None) useOptimistic
use_action_state(action, initial, permalink=None) useActionState
use_effect_event(fn) useEffectEvent
use_debug_value(value, formatter=None) useDebugValue
use(promise_or_context) use
use_form_status() React DOM useFormStatus

Return tuples follow React: state/setter, state/dispatch, pending/start, or state/action/pending. A setter accepts a value or functional updater. Refs have a mutable .current. Callback wrappers preserve React identity in dependency arrays. start_transition(fn) is available outside hooks.

Dependencies are compared by React's Object.is, with Python object identity preserved through boxes. None means no dependency array (run every commit), [] means an empty array (mount/unmount). Effects must return None or a cleanup function, not a coroutine. For example:

def effect():
    connection = connect()
    return connection.close
use_effect(effect, [])

Effect events should only be invoked from effects, as in React. There is no Python static linter enforcing that rule in this release.

Async data and actions

import asyncio
from pyreact_forge import component, html, resource, suspense

async def load(key):
    await asyncio.sleep(0.1)
    return {"message": "resolved"}

messages = resource(load)  # Create a stable resource outside the render.

@component
def Content():
    data = messages.read("example")
    return html.p(data["message"])

@component
def App():
    return suspense(Content(), fallback=html.p("Loading…"))

Resource.preload(key) returns a stable promise holder, .read(key) suspends, .invalidate(key) evicts one result, and .clear() evicts all results. Keys must be hashable. Invalidation does not itself schedule a React render. Rejected resources reach the nearest error boundary.

For optimistic transitions, do the optimistic update synchronously in the action before returning an awaitable. Python async functions start later in the event loop, so updates inside the coroutine do not automatically retain the synchronous transition context. Wrap updates after awaits in start_transition as React's async transition rules require. examples/conformance/app.py contains the tested pattern.

Class components

from pyreact_forge import ClassComponent, class_component, html

@class_component
class Counter(ClassComponent):
    def __init__(self, props):
        super().__init__(props)
        self.state = {"count": 0}

    def render(self):
        return html.button(
            self.state["count"],
            on_click=lambda: self.set_state(
                lambda previous: {"count": previous["count"] + 1}
            ),
        )

State is a dictionary. set_state merges a partial dictionary; force_update is available. Supported lifecycle adapter names are component_did_mount, component_did_update, component_will_unmount, component_did_catch, should_component_update, get_snapshot_before_update, and static get_derived_state_from_props / get_derived_state_from_error. PureComponent uses the React pure base class. Legacy UNSAFE_* lifecycles are not adapted. Class refs currently expose the JS adapter rather than transparently returning the Python instance.

Roots, DOM APIs and routing

create_root(container="root"), hydrate_root(container, node), Root.render(node), Root.unmount(), and mount(node, strict=False) adapt the client renderer. The optional identifier_prefix must match server/client settings. flush_sync(fn) invokes React DOM's synchronous flush.

preload, preload_module, preinit, preinit_module, preconnect, prefetch_dns and request_form_reset expose DOM helpers. Their keyword options are JS option names (for example crossOrigin); use **{"as": "style"} where a JS option collides with a Python keyword.

cache, cache_signal and add_transition_type are thin upstream adapters. Server-component cache semantics are not provided by the classic SSR CLI. runtime_capabilities() returns the bundled React exports.

router.Link, router.Router, router.navigate, router.use_location, and router.match_path are optional. Route patterns support literal paths, :parameters and trailing *. Query/hash are preserved in locations and ignored when matching route paths. This is a small router, not React Router parity.