defineCustomFieldsConfig is Mercur’s model-scoped extension surface. From one file per model you add validated fields to built-in create and edit forms, replace, remove, or add fields in detail sections, and add columns to the list table. Everything wires into the built-in page and stays typed against the model’s generated registry.
UI, not schema. This helper is a panel surface. It renders, validates, and displays fields. It does not create database columns. To store extra data, use the backend Custom Fields module or your own API route or workflow. In the MVP, panel custom fields for
product are submitted under additional_data and persisted onto the product’s metadata.What you’ll build
AnERP ID field on the vendor product edit form, shown in the product’s detail section and as a list-table column, all from a single src/custom-fields/product.tsx.
Register the typed targets
Models, form zones, display zones, and built-in field ids are typed per panel. You register them once, andcreate-mercur-app ships this reference for you.
apps/vendor/src/extension-targets.d.ts
Build the config
1
Create the model file
Drop
src/custom-fields/<model>.tsx and default-export a defineCustomFieldsConfig. createFormHelper from @mercurjs/dashboard-shared turns a Zod schema into an input type plus validation.apps/vendor/src/custom-fields/product.tsx
2
Add a detail-section display
displays[] targets a detail-page section by its zone id. Keyed by field id, an entry adds, replaces, or removes a field.apps/vendor/src/custom-fields/product.tsx
3
Add a list column
The
list block extends the model’s list table. Add or override columns by id, hide built-in columns, and reorder.apps/vendor/src/custom-fields/product.tsx
4
Reload the panel
Open the vendor portal. The product edit drawer shows the ERP ID field, validated on submit and persisted via
additional_data. The detail general section shows the ERP ID row, with subtitle removed and handle re-rendered. The product list shows the ERP column.The createFormHelper surface
createFormHelper<T>() exposes a Zod-based surface that drives both the input type and its validation.
Form.Field → Form.Item chain, never a raw Controller. They participate in the existing TabbedForm and RouteDrawer submit and validation flow.
Linked-module data
To read data from a linked module alongside the entity, declare it withlink. Those relations are fetched with the entity and become available to columns and displays.
The SDK derives the fetch query from
link and merges it into the built-in query with the + and - convention. You never hand-write the field list.Verify
- The ERP ID field renders in the product edit drawer and validates on submit.
- Saving persists the value, visible on reload, via
additional_datatometadata. - The detail general section shows the ERP row, hides
subtitle, and re-rendershandle. - The product list shows the ERP column, hides
collection, and reorders columns. - Set
zone: "nope"informs.bun run lint(tsc) fails against the model’s registry.
FAQ
Which models and zones are available?
Which models and zones are available?
Today: the
product model in the vendor portal, with form zone edit and display zone general. The valid set per panel is generated into CustomFieldsRegistry in extension-targets.d.ts. Autocomplete model and zone to see what’s mounted.Where is the value actually stored?
Where is the value actually stored?
In the MVP, product custom fields are submitted under
additional_data and persisted onto product.metadata. defineCustomFieldsConfig itself doesn’t create a column. For durable, queryable storage, model it with the backend Custom Fields module or a custom route or workflow.Can I extend the onboarding wizard?
Can I extend the onboarding wizard?
That’s the same helper with
zone: "onboarding" and tab set to a wizard step id (vendor only). It’s designed but not mounted in the current MVP. The runtime host exists. The wizard mount is a follow-up.Can a block ship custom fields?
Can a block ship custom fields?
Yes. A block can include
src/custom-fields/ files in its vendor_ui or admin_ui entry, aggregated like the host app’s.Next steps
Custom Fields module
Persist extra data on an entity with a generated side table.
Add a widget
Inject a component at a built-in zone.