Skip to content

Commit 40dc0ca

Browse files
committed
docs(reference): make the jinja component example actually mount
The shipped example violated the rule stated three lines below it on the same page: home.html asked for {% component "pages.counter" %} and {% component "pages.badge", class="badge" %}, but main.py defined counter in __main__ and registered root_components=["main.counter"]. Both tags emitted markup for a path ReactPy could not import -- the elements stayed empty and the only symptom was 'ImportError: ReactPy failed to import "pages"' on the websocket request, which a reader copying the page never sees. Add the missing pages.py (counter + badge), register both paths, and show the third file in a tab. Verified in Chrome: both mounts connect, the counter increments 0 -> 1.
1 parent d0026fb commit 40dc0ca

3 files changed

Lines changed: 33 additions & 16 deletions

File tree

‎docs/examples/reference_jinja/component_tag/main.py‎

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,9 @@
33
from starlette.routing import Route
44
from starlette.templating import Jinja2Templates
55

6-
from reactpy import component, html, use_state
76
from reactpy.executors.asgi import ReactPyMiddleware
87
from reactpy.templatetags import ReactPyJinja
98

10-
11-
@component
12-
def counter():
13-
count, set_count = use_state(0)
14-
return html.div(
15-
html.h1(count),
16-
html.button({"onClick": lambda event: set_count(count + 1)}, "Add 1"),
17-
)
18-
19-
209
# start
2110
templates = Jinja2Templates(
2211
env=Environment(
@@ -33,7 +22,9 @@ async def home(request):
3322

3423
starlette_app = Starlette(routes=[Route("/", home)])
3524

36-
# The dotted path must be importable by the server: the browser asks ReactPy to
37-
# render `pages.counter`, and that lookup happens on the server, not in the
38-
# template. Register the same path here that the template passes to the tag.
39-
my_app = ReactPyMiddleware(starlette_app, root_components=["main.counter"])
25+
# The dotted path in the template is resolved on the server, not in the
26+
# template: the browser asks ReactPy to render `pages.counter`, and ReactPy
27+
# imports it to satisfy that request. Register exactly the paths the template
28+
# passes to `{% component %}` -- here the `counter` and `badge` functions in
29+
# the sibling `pages.py`.
30+
my_app = ReactPyMiddleware(starlette_app, root_components=["pages.counter", "pages.badge"])
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from reactpy import component, html, use_state
2+
3+
4+
@component
5+
def counter():
6+
count, set_count = use_state(0)
7+
return html.div(
8+
html.h1(count),
9+
html.button({"onClick": lambda event: set_count(count + 1)}, "Add 1"),
10+
)
11+
12+
13+
@component
14+
def badge():
15+
# The tag's `class` argument styles the mount element, not this markup, so
16+
# the component is free to render whatever it likes inside it.
17+
return html.div(
18+
html.span({"class": "badge-label"}, "Status:"),
19+
html.span({"class": "badge-value"}, "active"),
20+
)

‎docs/src/reference/jinja.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,19 @@ Both are raised at render time, so they surface as a 500 on the page — not as
138138
{% include "../../examples/reference_jinja/component_tag/home.html" %}
139139
```
140140

141+
=== "pages.py"
142+
143+
```python
144+
{% include "../../examples/reference_jinja/component_tag/pages.py" %}
145+
```
146+
141147
=== ":material-play: Run"
142148

143149
```python
144150
# TODO
145151
```
146152

147-
The component must live in an importable module, since the browser reaches it by dotted path — a component defined inside the view function is not reachable that way. The template's path and the `#!python root_components` list have to agree exactly.
153+
The components cannot live in `#!python main.py` even though that is where the wiring is. The browser reaches them by dotted path, so they must sit in a module the server can import — a component defined next to the view is reachable as `#!python main.counter`, but then the template has to say exactly that. Whatever you choose, the path in `{% component %}` and the entry in `#!python root_components` must agree character for character. Nothing checks it for you: a mismatch emits markup that mounts an element which stays empty forever, and the failed import surfaces only as a server-side traceback on the websocket request.
148154

149155
### A client-side component
150156

0 commit comments

Comments
 (0)