React Generator¶
The React Generator produces a modern React frontend application from your GUI model. It is used internally by the Full Web App Generator generator to create the frontend. It is also one of the Spec-Driven Agent’s generator tools, so an agentic run can produce this frontend and then customise it further.
Overview¶
The React Generator creates:
Pages: React components for each screen in your GUI model
Components: Reusable UI components (tables, forms, charts)
Contexts: React contexts for state management
Routing: Application navigation setup
Generated Components¶
TableComponent¶
Dynamic data tables with:
Sorting and filtering
Pagination
Add/Edit modals with form validation
Backend validation error display (keeps modal open on error)
Support for all relationship types (1:1, 1:N, N:M)
Lookup fields for related entities; the Add/Edit dialog offers only the relationships the backend
<Entity>Createschema accepts (the same rule as the Pydantic generator), so the non-owning side of a 1:1 association has no field whose value the backend would dropDisplay-only form fields: a
formColumnsentry with"readOnly": trueis shown in the Add/Edit dialog but cannot be edited, is not validated as required, and is left out of the POST/PUT body (the server owns the value)
MethodButton¶
Executes class and instance methods with:
Parameter input modal for methods with parameters
Automatic type handling (int, float, bool, string, date, time)
Detailed error display for 500 errors
Modal stays open on error for retry
Automatic table refresh after execution
CrudButton¶
A Button whose actionType is Create, Update or Delete
acts through the table bound to its entity, reusing that table’s dialog,
endpoint and selected row:
Create opens the table’s Add dialog (POST
/<entity>/).Update opens the Edit dialog on the table’s selected row (PUT).
Delete deletes the selected row (DELETE), after a confirmation prompt when
confirmation_requiredis set.
The table is the button’s instance_source or, failing that, the table
bound to the button’s entity_class on the same screen. A create button
with no such table on its screen navigates to the first screen that has one
and opens its dialog there. Update and delete report “Select a row in the
table first” when nothing is selected. A CRUD button with no bound table is
rendered as a plain button.
FormBlock¶
A Form bound to a class (Form.data_binding) is rendered as a
FormBlock around its fields. Submitting it POSTs /<class>/ with the
value of every input bound to an attribute (typed from the attribute: int,
float, bool, text; an empty field is left out so the backend applies its
default), shows a success or error message (the backend’s validation detail),
and clears the form on success. Fields keep their labels; the submit button
shows the form’s submit_label. An unbound form is rendered as a plain
<form> that does not submit.
Chart Components¶
Visualization components including:
Line charts
Bar charts
Pie charts
Data binding to backend entities
Aggregation: a series with an
aggregationgroups the records by its label field and shows one value per group (count,sum,avg,min,max,median,first,last); a bound series without a value field counts the records per label
Metric cards (MetricCardBlock) show their binding’s aggregation over all
records; without one, the sum of the bound field, or the number of records when
no field is bound.
MapBlock¶
Interactive multi-layer map powered by Leaflet (used
directly, no wrapper library) with OpenStreetMap tiles (no API key required).
Each MapLayer on the metamodel Map component produces one rendered layer
in the generated output. Popup labels come from database rows and are always
HTML-escaped. Supported layer types:
points —
L.marker+ popup per row (lat/lng columns required).geojson —
L.geoJSON(geometry column required).choropleth —
L.geoJSONwith a value-driven fill colour + auto legend (geometry + value columns required; degrades to plain GeoJSON with a console warning when the value column is absent).heatmap —
leaflet.heatheat layer (lat/lng columns required; weight column optional).
The generated MapBlock.tsx loops over the layers prop and dispatches to the
correct per-type renderer. Each renderer is preceded by a section-comment banner
(// ===== POINTS LAYER =====, etc.) and extension blocks are left as commented
examples, so the output is self-documenting. See Map Component Reference for the full guide.
The following npm packages are automatically added to the generated package.json
when the GUI model contains at least one Map component:
Package |
Version |
Purpose |
|---|---|---|
|
|
Core Leaflet mapping library (BSD-2-Clause), used directly. |
|
|
Heat-map plugin (used when any layer is |
|
|
TypeScript type definitions for Leaflet. |
|
|
TypeScript type definitions for leaflet.heat. |
Usage¶
The React Generator is typically used via the Full Web App Generator:
from besser.generators.web_app import WebAppGenerator
gen = WebAppGenerator(domain_model, gui_model, output_dir="output")
gen.generate()
Or standalone:
from besser.generators.react import ReactGenerator
gen = ReactGenerator(domain_model, gui_model, output_dir="frontend")
gen.generate()
Generated Structure¶
frontend/
├── src/
│ ├── components/
│ │ ├── table/
│ │ │ └── TableComponent.tsx
│ │ ├── charts/
│ │ ├── runtime/
│ │ │ ├── TableBlock.tsx
│ │ │ ├── ChartBlock.tsx
│ │ │ ├── DataListBlock.tsx
│ │ │ ├── MetricCardBlock.tsx
│ │ │ ├── CrudButton.tsx
│ │ │ ├── FormBlock.tsx
│ │ │ └── MapBlock.tsx
│ │ ├── InputComponents.tsx
│ │ └── MethodButton.tsx
│ ├── contexts/
│ │ └── TableContext.tsx
│ ├── pages/
│ │ └── <PageName>.tsx
│ ├── App.tsx
│ ├── design.css
│ └── index.tsx
├── public/
├── index.html
├── vite.config.ts
├── package.json
└── tsconfig.json
One .tsx file is written per page in the GUI model, named after that page
(PascalCase), and App.tsx wires them into the router. MapBlock.tsx is
only emitted when the model contains a Map component.
Design stylesheet¶
When the GUI model has a non-empty stylesheet (see GUI model),
it is written verbatim to src/design.css and imported from src/index.tsx
after App, so the design’s class, :root, @media and :hover rules
apply to the generated pages and win ties with the components’ own CSS. Components
render their css_classes as className; tables, charts and metric cards
forward it through their runtime blocks to the rendered root element.
Per-element Styling is still emitted as inline styles. Without a stylesheet
neither the file nor the import is generated.