drf-react-by-schema

DRF and React by Schema is a pair of packages — drf-react-by-schema-django (server) and drf-react-by-schema (client) — that add schema-driven CRUD to a Django REST Framework backend. Django describes each model; React renders forms, validation, lists and detail views from that description.
How it works
- Django/DRF owns the data model. Models, serializers and viewsets stay on the server.
drf-react-by-schema-djangoderives a per-model schema (field types, labels, required/read-only flags, choices, relations, validators, layout) and serves it throughOPTIONS, or writes it to disk with thesync_reactcommand. - React renders from the schema.
drf-react-by-schemareads the schema and builds the matching UI: a fieldtypeselects the form widget, the DataGrid cell, its inline editor and the yup validation rule. No per-model form code. - One source of truth. Changing a Django model is enough to change the React forms, columns and validation. Widgets, layout, labels and validation can still be customized per app.
Published packages: npm drf-react-by-schema and PyPI drf-react-by-schema.
API reference
Every exported component (purpose and props), the full useAPIWrapper() API (data,
auth, form state, dialogs, feedback), the shared prop building blocks
(fieldsLayout, fieldsProps, sumRows, actions, …) and the utility functions are
documented in docs/API_REFERENCE.md.
Consuming apps must use useAPIWrapper(). The lower-level useDRFReactBySchema() is
internal, is not exported from the package entry and must not be used by app code.
Field types & widgets
Every schema field type maps to a specific widget. This includes the standard
types (text, number, choices, dates, booleans, files, images, …) and the PostGIS
geometry fields (point, polygon, multipoint, multipolygon,
geometrycollection), which render an interactive Leaflet map picker/drawer with a
read-only thumbnail map in the DataGrid.
See the complete table of field types and their widgets (including the geometry
behaviours — add/move/remove rules, GeoJSON text sync and fallbacks) in
docs/FIELD_TYPES.md.
Conditional visibility (conditional_visible)
A field can be shown/hidden from the <Form>/FormBySchema and the DataGrid inline
editor depending on the value of other fields. While hidden, its value is cleared on save
(and in the form). The schema key conditional_visible, produced by the Django package's
custom fields, accepts these shapes:
a single
[sourceField, sourceValue]pair — a single condition, e.g.["type", "book"];a plain array of conditions — combined with OR (show if any matches), the legacy fallback;
an explicit
{ operator: 'AND' | 'OR', conditions: [...] }expression for arbitrary, parenthesised logic.conditionsmay mix pairs and nested expressions, e.g. showisbnonly when the recordtypeisbookand theformatis eitherhardcoverorpaperback:{ "operator": "AND", "conditions": [ ["type", "book"], { "operator": "OR", "conditions": [ ["format", "hardcover"], ["format", "paperback"] ] } ] }
A plain array of conditions is always OR; use an explicit { operator: 'AND', ... }
when every condition must hold. Combined with ui_required, a conditionally visible field
is required only while it is actually shown.
Scoping autocomplete options (optionsACQueryParams)
The package loads relation-field options automatically. To send static query params
with those fetches — e.g. to make a backend filter_by_callback scope a relation to a
parent record — pass optionsACQueryParams:
const params = ufpaId ? [`ufpa_id=${ufpaId}`] : undefined;
<Form
model="linha_de_base"
optionsACQueryParams={
params && {
responsavel: params, // a top-level relation field
formas_acesso_terra: { em_nome_de_quem: params }, // an inline formset's child field
}
}
/>
A relation field maps to a string[]; a writable inline formset maps to a nested record
keyed by its child fields under its parent field key. It works on Form,
FormBySchema, InlineFormsetFieldBySchema, the grids (GenericModelList,
GenericRelatedModelList, DataGridBySchemaEditable) and the related-edit dialog
(DialogFormBySchema). It is the static counterpart of the per-row filterOptionsAC.
Full details: docs/API_REFERENCE.md §4.16.
Localization & theming
User-facing strings (geometry picker, form/dialog action buttons, …) are exposed through
the MUI theme (theme.texts), so you can localize every widget in your own theme.ts
— no provider changes. The package ships with sensible defaults and also localizes the
MUI components it renders (DataGrid, date pickers) to pt-BR.
See the full reference (namespaces, precedence, MUI locale setup, custom markers) in
docs/localization.md.
Authentication, password reset & e-mail
The package ships ready-made pieces for a forgot/change password flow over the
JWT + simplejwt stack: the ForgotPasswordForm and ChangePasswordForm components,
plus requestPasswordReset, validatePasswordReset, confirmPasswordReset and
changePassword helpers on
useAPIWrapper(). They talk to the endpoints provided by the Django package
(password_reset/request|validate|confirm, my_account/change_password).
See how to wire them up, the server contract, and how to set up e-mail/SMTP for the
reset link in docs/password-reset-and-email.md.
Getting started
Build the Django project and models (guide: https://pypi.org/project/drf-react-by-schema/).
Create the
frontendfolder that will hold the React app.Install the required peer dependencies:
yarn add @emotion/react@^11 @emotion/styled@^11 @hookform/resolvers@^3 @mui/icons-material@^6 @mui/material@^6 @mui/x-data-grid@^7 @mui/x-date-pickers@^7 axios@^1 dayjs@^1 react@^18 react-dom@^18 react-hook-form@^7 react-number-format@^5 string-mask@^0.3.0 yup@^1
Install the optional widget peers only for the features you use:
# geometry (PostGIS) pickers
yarn add leaflet@^1.9 react-leaflet@^5 leaflet-draw@^1.0.4
# rich-text fields (model_html)
yarn add @tiptap/react@^3 @tiptap/starter-kit@^3 dompurify@^3
- Install
drf-react-by-schema:
yarn add drf-react-by-schema
- Wrap the app with
DRFReactBySchemaProvider, passing the Django endpoints and the theme:
// App.tsx
import { DRFReactBySchemaProvider } from 'drf-react-by-schema';
import * as schemas from './drfReactBySchema';
import { paginationConfig } from './drfReactBySchema/pagination';
import { theme } from './theme';
export default function App() {
return (
<DRFReactBySchemaProvider
serverEndPoint={{
url: 'http://localhost:8000',
apiTokenUrl: 'http://localhost:8000/api/token/',
}}
schemas={schemas}
pagination={paginationConfig}
theme={theme}>
<YourWonderfulApp />
</DRFReactBySchemaProvider>
);
}
schemas and paginationConfig are generated by sync_react (see below) and are optional: without them the provider fetches each schema with OPTIONS and uses the package's default pagination.
- Build the pages from the package components. Start with a list and a form:
<GenericModelList model="produto" paginationMode="server" />
<Form model="produto" objId={id} />
See docs/API_REFERENCE.md for every component and the useAPIWrapper() surface.
Syncing React with Django
drf-react-by-schema-django ships the sync_react management command. Add a script to package.json, using the Python executable of your virtual environment:
"scripts": {
"sync-react": "cd ../ && ./venv/bin/python manage.py sync_react"
}
Or, with the virtualenv activated:
"scripts": {
"sync-react": "cd ../backend && python manage.py sync_react"
}
It writes the src/drfReactBySchema/ folder (path configurable through DRF_REACT_BY_SCHEMA['REACT_APP_DIR']):
schemas/<Model>Schema.json— one schema per model;types.ts— TypeScript interfaces and enums for the models;optionsGetters.ts— label helpers for the generated enums;index.ts— one<Model>Schemaloader per model, imported as a single object (import * as schemas);pagination.ts— thepaginationConfigobject.
Import model types from drfReactBySchema/types to type your components and data.