Tutorial 4: Custom Form/Table Renderers for Level
BasicIn this tutorial, one prompt lets the user express a simple product need: the level field in the Student experience should feel less generic and more like a real training-stage workflow.
From that user need, AI can infer that the next step is no longer just relabeling the field. It now needs a more dedicated rendering experience owned by the demo-student module.
This tutorial covers the reverse chain custom-resource handoff branch of the contract loop.
NOTE
Like the earlier tutorials in this series, this page keeps using the standalone demo-student sandbox. That keeps the custom-renderer experiment isolated from the repo's real suite-owned a-training/training-student implementation.
Goal
By the end of this tutorial, you will understand how a user-facing request about a more business-specific level experience turns into a concrete custom-renderer implementation.
In particular, you will understand:
- when built-in render metadata is enough and when a custom renderer is worth adding
- how a frontend table cell bean and a frontend form-field component can both serve the same backend field
- how backend metadata points to module-owned frontend render resources
AI Prompt
Give AI a prompt like this:
For demo purposes, use custom renderer components to give the `level` field on the Student list page and form a more distinctive look and feel.Why this step matters
This is the right follow-up step because built-in render resources are a good starting point, but some fields eventually need a more distinctive user experience to match the business workflow.
The level field is a good teaching example because users often expect two improvements once a training workflow becomes more realistic:
- the list should make each training stage easier to recognize at a glance
- the form should guide the user more clearly when choosing a training stage
This is where Cabloy’s contract model becomes more practical: the backend field still owns the business contract, while the frontend module progressively deepens the UI behavior behind that contract.
CLI commands to inspect/use
Inspect the Zova create surface first:
npm run zova :create
npm run zova :create:bean --help
npm run zova :create:component --helpRepresentative generation commands for this tutorial:
npm run zova :create:bean tableCell level -- --module=demo-student
npm run zova :create:component formFieldLevel -- --module=demo-studentUsage notes:
- use
:create:beanwhen AI determines that the list needs a dedicated table-cell render resource under the bean scene - use
:create:componentwhen AI determines that the form needs a custom frontend component/controller surface - generation gives you the structural starting point, but this tutorial still expects manual refinement so the result matches the
demo-studentteaching implementation - after frontend resources exist, return to the backend entity and point
ZovaRender.field(...)andZovaRender.cell(...)at the custom module resources - once backend metadata starts consuming those new frontend resources, treat the next step as a reverse fullstack handoff rather than frontend-only cleanup: refresh generated output, rebuild the relevant flavor, and re-sync Vona dependencies
Generated or affected files
To satisfy the user-facing need above, AI will usually converge on a small set of implementation anchors like these:
- custom table cell bean:
zova/src/module/demo-student/src/bean/tableCell.level.tsx
- custom form-field controller:
zova/src/module/demo-student/src/component/formFieldLevel/controller.tsx
- form-field metadata wrapper:
zova/src/module/demo-student/src/.metadata/component/formFieldLevel.ts
- backend field contract to update:
vona/src/module/demo-student/src/entity/student.tsx
Representative metadata targets after AI makes that implementation decision are:
ZovaRender.field('demo-student:formFieldLevel', {
items: levelItems,
helper: $locale('LevelPlaceholder'),
});and:
ZovaRender.cell('demo-student:level', { items: levelItems });This is the point where a user request about “better list presentation” and “better form guidance” starts turning into explicit renderer resources and backend metadata links.
How those files support the user experience
These files work together to deliver the richer level experience:
tableCell.level.tsxmakes the training stage easier to recognize in the listcomponent/formFieldLevel/controller.tsxmakes the form interaction more guided and business-specific.metadata/component/formFieldLevel.tsexposes that component through the module registration surfaceentity/student.tsxremains the backend-owned business contract that chooses which frontend resources should render the field
That means the backend still defines the business field, validation, and metadata entry point, while the frontend module owns the implementation details that make the experience feel more tailored to the Student Training Center workflow.
Verification
These checks are the reverse-chain synchronization steps AI must complete so the user-facing renderer change is actually available to backend consumers and the running app.
- refresh the generated handoff surfaces before checking backend consumers:
npm run zova :tools:metadata demo-student
npm run build:zova:admin
npm run deps:vonaIf the same renderer path must also be available for Web, also run:
npm run build:zova:web
npm run deps:vonaIf backend-side type consumers still cannot see the new shared renderer types even though generated .zova-rest output is already correct, rebuild vona/node_modules and reinstall dependencies:
cd vona && rm -rf node_modules && pnpm install- make sure the local dev workflow is running:
npm run dev- open
http://localhost:7102/admin/ - enter the Student list page and verify that the
levelcolumn now uses the custom table-cell presentation - open a Student create, update, or view form and verify that the
levelfield now uses the custom form-field behavior - inspect
vona/src/module/demo-student/src/entity/student.tsxand confirm that the backend metadata now points todemo-student:formFieldLevelanddemo-student:level
Read more
- Frontend Metadata Back to Backend
- Frontend CLI
- Component Guide
- Zova Form Under the Hood
- Bean Scene Boilerplate Variants
Next step
Continue to Tutorial 5: Backend Contract Sharing.