Skip to content

Commit 686456d

Browse files
committed
Expand React on Django rendering and docs
1 parent c38e2bd commit 686456d

101 files changed

Lines changed: 14473 additions & 55 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
unit:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
python-version: ["3.10", "3.13"]
16+
django-version: ["4.2.*", "5.1.*"]
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: actions/setup-python@v5
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Install Python dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
python -m pip install "Django==${{ matrix.django-version }}" -e .[dev]
29+
30+
- name: Ruff
31+
run: ruff check .
32+
33+
- name: Pytest
34+
run: pytest
35+
36+
example-e2e:
37+
runs-on: ubuntu-latest
38+
timeout-minutes: 20
39+
40+
steps:
41+
- uses: actions/checkout@v4
42+
43+
- uses: actions/setup-python@v5
44+
with:
45+
python-version: "3.13"
46+
47+
- uses: actions/setup-node@v4
48+
with:
49+
node-version: "22"
50+
cache: "npm"
51+
cache-dependency-path: example/package-lock.json
52+
53+
- name: Install Python dependencies
54+
run: |
55+
python -m pip install --upgrade pip
56+
python -m pip install -e .[dev]
57+
58+
- name: Install example dependencies
59+
working-directory: example
60+
run: npm ci
61+
62+
- name: Install Playwright browser
63+
working-directory: example
64+
run: npx playwright install --with-deps chromium
65+
66+
- name: Make example scripts executable
67+
run: chmod +x example/bin/dev example/bin/prod example/bin/test-ci
68+
69+
- name: Run example smoke and Playwright suite
70+
working-directory: example
71+
run: ./bin/test-ci

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,12 @@ __pycache__/
1111
build/
1212
dist/
1313
htmlcov/
14-
14+
.playwright-cli/
15+
example/db.sqlite3
16+
example/node_modules/
17+
example/public/packs/
18+
example/public/packs-test/
19+
example/playwright-report/
20+
example/test-results/
21+
example/tmp/
22+
output/

README.md

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,111 @@ This initial scaffold covers:
99
- package metadata and tooling
1010
- Django settings-backed configuration
1111
- safe JSON and HTML output helpers
12+
- `django-rspack` integration helpers for bundle tags and asset lookup
1213
- client-only component rendering
14+
- server-side rendering over the shared node renderer protocol
15+
- streaming SSR and RSC response adapters
1316
- Django template tags for `react_component` and `react_component_hash`
17+
- store hydration helpers via `redux_store` and `redux_store_hydration_data`
18+
- a `server_render_js` helper for raw node-side evaluation
1419
- pytest coverage for the initial API surface
1520

16-
`django-rspack` is intentionally kept at the boundary for now. The rendering
17-
core does not assume a specific asset loader yet, so we can plug in the bundler
18-
without rewriting the component API.
21+
## Example app
22+
23+
The repository now includes a runnable Django example at `example/`. It ports
24+
the supported client-rendering slice of `react_on_rails_pro/spec/dummy` and
25+
now also exercises the live SSR, streaming, and RSC adapters.
26+
27+
- the client-side HelloWorld page
28+
- the `server_render_js` page for non-React server evaluation
29+
- the `react_component_hash` metadata page
30+
- a multi-component index page
31+
- the shared-store pages for deferred and server-rendered hydration
32+
- the HTML options example, including JSON-string props and nested `data-*` attributes
33+
34+
The example uses `django-rspack` for manifest-backed asset lookup and the shared
35+
`react-on-rails` npm runtime for browser mounting and hydration.
36+
37+
Run the main validation paths with:
38+
39+
```bash
40+
cd example && npm install
41+
./bin/test-ci
42+
./bin/dev dev
43+
./bin/dev static
44+
./bin/prod
45+
```
46+
47+
Useful routes in the example app:
48+
49+
- `/`
50+
- `/client_side_hello_world/`
51+
- `/server_side_hello_world/`
52+
- `/server_side_hello_world_shared_store/`
53+
- `/server_render_js_example/`
54+
- `/metadata_example/`
55+
- `/streaming_hello_world/`
56+
- `/rsc_hello_world/`
57+
- `/client_side_hello_world_with_options/`
58+
59+
The Python package also exposes the helper APIs directly:
60+
61+
```python
62+
from react_on_django import (
63+
redux_store,
64+
redux_store_hydration_data,
65+
render_react_component,
66+
render_react_component_hash,
67+
server_render_js,
68+
)
69+
```
70+
71+
The Django app now also ships management commands for starter scaffolding:
72+
73+
```bash
74+
python manage.py react_install
75+
python manage.py react_generate dashboard-card
76+
python manage.py react_generate posts-feed --rsc
77+
```
78+
79+
## Using with django-rspack
80+
81+
`react-on-django` uses `django-rspack` for compiled asset lookup. The component
82+
renderer stays separate from the bundler, but React on Django exposes helpers so
83+
the two packages fit together cleanly.
84+
85+
```python
86+
# settings.py
87+
INSTALLED_APPS = [
88+
...,
89+
"django_rspack",
90+
"react_on_django",
91+
]
92+
93+
REACT_ON_DJANGO = {
94+
"bundle_name": "application",
95+
}
96+
```
97+
98+
```django
99+
{% load react %}
100+
<!DOCTYPE html>
101+
<html>
102+
<head>
103+
{% react_component_assets %}
104+
</head>
105+
<body>
106+
{% react_component "HelloWorld" props=hello_props %}
107+
</body>
108+
</html>
109+
```
110+
111+
The integration helpers are also available from Python:
112+
113+
```python
114+
from react_on_django.assets import (
115+
get_react_bundle_urls,
116+
get_server_bundle_path,
117+
render_react_component_assets,
118+
)
119+
```

docs/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# React on Django Docs
2+
3+
This directory is the canonical documentation source for `react-on-django`.
4+
5+
The public site will live in the separate `shakacode/react-on-django.com` repo.
6+
That site should sync content from this directory, prepare it for Docusaurus,
7+
and handle deployment, branding, and marketing pages.
8+
9+
Current docs are organized into:
10+
11+
- `introduction.md`
12+
- `getting-started/`
13+
- `guides/`
14+
- `pro/`
15+
- `sidebars.ts`
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Create a React on Django App
2+
3+
The package ships with both a reference example in `example/` and starter
4+
management commands.
5+
6+
Recommended starting points:
7+
8+
- run `manage.py react_install` for the starter JavaScript layout
9+
- run `manage.py react_generate` for new component scaffolds
10+
- copy the example bundle layout under `app/javascript/`
11+
- copy the Django settings seam for `django-rspack`
12+
- copy the example process scripts:
13+
- `example/bin/dev`
14+
- `example/bin/prod`
15+
- `example/bin/test-ci`
16+
17+
Minimum directories:
18+
19+
```text
20+
your-project/
21+
app/javascript/components/
22+
app/javascript/packs/
23+
public/packs/manifest.json
24+
templates/
25+
```
26+
27+
Starter command examples:
28+
29+
```bash
30+
python manage.py react_install
31+
python manage.py react_generate dashboard-card
32+
python manage.py react_generate posts-feed --rsc
33+
```
34+
35+
The example app is still the reference for richer flows such as shared stores,
36+
streaming shells, and the current RSC demos.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Installation
2+
3+
Install the package and its Django integration dependencies:
4+
5+
```bash
6+
python -m pip install react-on-django django-rspack
7+
```
8+
9+
Add the apps to `INSTALLED_APPS`:
10+
11+
```python
12+
INSTALLED_APPS = [
13+
"django.contrib.staticfiles",
14+
"django_rspack",
15+
"react_on_django",
16+
]
17+
```
18+
19+
Add the request middleware so the current request is available to the render
20+
helpers:
21+
22+
```python
23+
MIDDLEWARE = [
24+
"django.middleware.common.CommonMiddleware",
25+
"react_on_django.middleware.ReactOnDjangoRequestMiddleware",
26+
]
27+
```
28+
29+
Configure the bundler manifest and React on Django settings:
30+
31+
```python
32+
RSPACK = {
33+
"manifest_path": BASE_DIR / "public" / "packs" / "manifest.json",
34+
}
35+
36+
REACT_ON_DJANGO = {
37+
"bundle_name": "application",
38+
"server_bundle_js_file": "server-bundle.js",
39+
"rsc_bundle_js_file": "rsc-bundle.js",
40+
"rendering_server_url": "http://localhost:3500",
41+
}
42+
```
43+
44+
Finally, load the assets in your base template:
45+
46+
```django
47+
{% load react %}
48+
{% react_component_assets %}
49+
```
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Quick Start
2+
3+
This is the shortest path from a Django template to a mounted React component.
4+
5+
## 1. Register a browser bundle
6+
7+
Build a client bundle that registers your components with the shared runtime:
8+
9+
```jsx
10+
import RuntimeBridge from "react-on-rails/client";
11+
import HelloWorld from "../components/HelloWorld";
12+
13+
RuntimeBridge.register({ HelloWorld });
14+
```
15+
16+
## 2. Render assets in the layout
17+
18+
```django
19+
{% load react %}
20+
<!doctype html>
21+
<html>
22+
<head>
23+
{% react_component_assets %}
24+
</head>
25+
<body>
26+
{% block content %}{% endblock %}
27+
</body>
28+
</html>
29+
```
30+
31+
## 3. Render a component from a template
32+
33+
```django
34+
{% extends "base.html" %}
35+
{% load react %}
36+
37+
{% block content %}
38+
{% react_component "HelloWorld" props=hello_props id="hello-world-root" %}
39+
{% endblock %}
40+
```
41+
42+
## 4. Pass props from the Django view
43+
44+
```python
45+
def homepage(request):
46+
return render(
47+
request,
48+
"homepage.html",
49+
{"hello_props": {"helloWorldData": {"name": "Ada"}}},
50+
)
51+
```
52+
53+
## 5. Enable SSR when ready
54+
55+
```django
56+
{% react_component "HelloWorld" props=hello_props prerender=True %}
57+
```
58+
59+
That only requires a renderer server once you actually enable prerendering.

0 commit comments

Comments
 (0)