ReactPy
Overview¶
ReactPy is ReactPy's standalone ASGI application. It serves the HTML document, the client bundle, and the websocket through which components render.
from reactpy.executors.asgi import ReactPy
my_app = ReactPy(root_component)
Reference¶
ReactPy(root_component, **settings)¶
Instantiate ReactPy once, at module level, and hand the instance to any ASGI server:
uvicorn main:my_app
ReactPy serves the page at every route that is not claimed by its own static or websocket paths.
from reactpy.executors.asgi import ReactPy
Parameters¶
-
root_component: The component to mount. A component constructor — the@componentfunction itself, not a call to it. This app is a single-page application: there is exactly one root. -
html_head(optional): Extra content for the document's<head>.Must be built with
html.headPass
html.head(...)— not a barehtml.title(...)or ahtml.div(...). Anything else raises at request time:ValueError: Head element must be constructed with `html.head`.Verified: this surfaces as a
500on the index route, not at import time, so a typo here only shows up once the server is running. -
prepend_body(optional): Content rendered at the start of the<body>, before the mount point. Accepts aVdomDictbuilt withhtml.*, orNoneto omit. Defaults tohtml.noscript("Enable JavaScript to view this site.").This is the one part of the tree that is genuinely server-rendered. See what
ReactPydoes and does not render. -
html_lang(optional): Value for the document'slangattribute. Defaults to"en". -
http_headers(optional): Extra response headers for the index document — a plaindict[str, str]. Use it for CSP,X-Frame-Options, caching, and similar. -
pyscript_setup(optional): Load PyScript into the document head, for running component code in the browser. Defaults toFalse.ReactPyCsrsets this for you. -
pyscript_options(optional): APyScriptOptionsconfiguring that behaviour. -
**settings: Any key fromReactPyConfig. Despite appearances these are not scoped to this app instance — each one is written to the matching global inreactpy.config, so the last executor constructed wins process-wide. See Configuration.
Returns¶
An ASGI application.
Usage¶
A standalone app¶
1 | |
1 | |
Serve it with uvicorn main:my_app. The name you assign matters, because it is what uvicorn module:name refers to.
Page metadata and headers¶
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 | |
Verified against a running server: lang="fr", the custom <title>, the custom response header, and the prepend_body markup all appear in the initial HTML.
Configuration¶
Settings are ordinary keyword arguments, and they reach the browser-side client:
1 2 3 4 5 6 7 8 9 | |
1 | |
| Setting | Default | Meaning |
|---|---|---|
path_prefix | /reactpy/ | Base path ReactPy serves its own static assets and websocket from. |
web_modules_dir | temp dir | Where built web modules are cached. |
reconnect_interval | 750 | Milliseconds before the first reconnect attempt. |
reconnect_max_interval | 60000 | Ceiling on the reconnect backoff. |
reconnect_max_retries | 150 | Attempts before giving up. |
reconnect_backoff_multiplier | 1.25 | Growth factor between attempts. |
async_rendering | True | Allow renders to overlap. |
debug | False | Server-side checks and richer error text — see the note below. |
max_queue_size | 1000 | Bound on the pending events of each handler target — the layout keeps a queue per target (core/layout.py, _event_queues), not one per connection. |
event_proxy_timeout | 5.0 | Seconds to wait for the browser to answer one live event read before EventProxyTimeout. |
event_proxy_max_depth | 10 | How deep one resolution may expand nested objects before they come back as references. |
event_proxy_max_timeouts | 3 | Consecutive unanswered round trips tolerated before live access is switched off for the connection. |
tests_default_timeout | 15.0 | Default timeout for reactpy.testing. Not settable this way — see the note below. |
Verified: setting path_prefix="/apps/reactpy/" moves asset serving to that prefix, and the reconnect values appear in the generated mount call. The three event_proxy_* settings were verified by constructing an executor with them and reading the matching reactpy.config options back.
They belong to a handler which reads the browser rather than to the client bootstrap: they bound the round trips behind await event.target.value. Reach for them only when a handler is timing out or a deeply nested read comes back as references — see Protocol Structure.
These keywords are process-wide, not per-app
executors/utils.py, process_settings walks the keywords and calls set_current() on the matching reactpy.config Option object. Those objects are module-level globals, so the last constructor to run wins for the whole process — including for any app built before it. Verified: two ReactPy instances created with reconnect_interval=111 and reconnect_interval=999 leave REACTPY_RECONNECT_INTERVAL.current at 999 for both. There is no per-app configuration layer; do not host two apps that need different values.
tests_default_timeout is worse than ineffective. Its Option is declared mutable=False in reactpy/config.py, and _option.py, set_current raises TypeError on an immutable option — so passing it raises Option(REACTPY_TESTS_DEFAULT_TIMEOUT=15.0) cannot be modified after initial load out of the constructor. Set it via the REACTPY_TESTS_DEFAULT_TIMEOUT environment variable before import, or not at all.
debug is not a client switch. REACTPY_DEBUG turns on server-side VDOM and JSON-attribute validation, populates the model's error text (without it, a failed component ships "error": "" and renders as nothing), and changes a few asset URLs to their ?dev forms. See Common Props.
Each setting also has an environment-variable twin in reactpy.config — REACTPY_PATH_PREFIX, REACTPY_RECONNECT_INTERVAL, and so on. Since the keyword already writes to that same global option, the two differ only in timing: the environment variable is read when the module is first imported, the keyword whenever an executor is constructed.
What ReactPy does and does not render¶
This is the distinction that trips people up, because the executor has historically been described as "SSR".
The initial HTML document contains an empty mount point:
<body>
<noscript>Enable JavaScript to view this site.</noscript>
<div id="app" class=""></div>
<script type="module" crossorigin="anonymous">
import { mountReactPy } from "/reactpy/static/index.js";
mountReactPy({ mountElement: document.getElementById("app"), ... });
</script>
</body>
Verified by fetching the index route: your root component's markup is not in the response. The browser connects over the websocket and ReactPy renders and streams from there.
What is server-rendered into the first response:
prepend_body, converted withreactpy_to_string.html_head.- The document shell:
lang, static asset paths, the mount script.
Practical consequences:
- Search engines and
curlsee an empty app. For content that must exist without JavaScript, render it yourself —reactpy_to_stringturns a component into an HTML string, which you can place inprepend_bodyor emit from your own route. It renders the component's first render directly, without a layout, so the component cannot use any hook:use_state,use_ref,use_effect,use_locationand the rest all raiseRuntimeError: No life cycle hook is active. Are you rendering in a layout?. Pass plainhtml.*elements, or a component that calls none of them. - There is no first-paint flash to optimise away, because there is no first paint to replace.
- A component that fails on the server fails after the page loads, leaving a blank
<div id="app">. When the page loads but shows nothing, check the server log before the browser console.
Not the same as ReactPyCsr
ReactPyCsr is the one that runs component code in the browser via PyScript. ReactPy runs components on the server and sends VDOM over a websocket. Both need a server; they differ in where your Python executes. See ReactPyMiddleware for mounting into an existing ASGI app.
Related¶
- Creating a Standalone React App — the tutorial version of this page.
- ReactPyMiddleware — mount components inside an existing ASGI application.
- ReactPyCsr — run component code in the browser.