Skip to main content
Custom fields extend a built-in model’s forms, detail sections, and list table without forking the page. You add a field to the create and edit forms, a row to the detail view, an action to a section menu, or a column to the list. You write one file per model under src/custom-fields/ and default-export a defineCustomFieldsConfig. The config targets a model, optionally fetches linked module data alongside it, and describes what to add to each surface.

Add a custom field

1

Create a custom-fields file

Create a file under src/custom-fields/ and default-export defineCustomFieldsConfig with the model you want to extend. Start with product.
apps/vendor/src/custom-fields/product.tsx
2

Describe the field with `createFormHelper`

Import createFormHelper from @mercurjs/dashboard-shared and turn a Zod schema into an input type and validation. The Zod schema drives both the default input and the validation.
apps/vendor/src/custom-fields/product.tsx
3

Add the field to a form

Add the field to a built-in form zone and tab. This injects an ERP ID field into the product edit form.
apps/vendor/src/custom-fields/product.tsx
4

Run the panel

Start the panel and open the product edit form. The field renders in its tab.
Terminal
For product, values submit under additional_data and persist onto the product’s metadata. See Persistence for other models.

Configuration

defineCustomFieldsConfig takes one object.

Add fields to a form

Inject fields into a built-in create, edit, or onboarding form.
Each field is a CustomFormField.
A form-field component receives no props. It renders as <Component /> inside the field’s additional_data.<field> React Hook Form context. Read and write the value with useFormContext() or useController(), and render through the Form.Field and Form.Item chain. Do not use a raw Controller. Values live in form state under additional_data. For product, custom fields persist onto the product’s metadata.

Extend detail sections

Add, replace, or remove fields on a detail section, and add actions to its ActionMenu.
fields[] is CustomDisplayField. A display component receives the loaded detail entity as data, including any linked module data. If the config declares link: "brand", read it off data.brand.
actions[] is SectionAction, the same shape as list bulkActions.

Extend the list table

Override or add columns, register bulk actions and filters, and set view defaults on the model’s list table.
columns[] is CustomColumn.
Bulk-action rendering is deferred in the MVP. bulkActions are accepted and surfaced by the config, but not yet mounted into the list toolbar.
The vendor product list is field-constrained. It must use the curated fields from useProductTableQuery. The SDK merges link fetches with the + and - convention, never bare fields, or the list returns a 500. You never hand-write the field list. The link declaration drives it.

Fetch linked module data

Declare link to fetch a module link alongside the entity. Its data rides on the data passed to displays and on the row passed to columns.

Persistence

The MVP is a UI surface only. Custom fields render, validate, and display through the built-in forms, sections, and tables. There is no generic core-side write path. For product, values submit under additional_data and persist onto metadata. To store data for other models, wire your own route or workflow, or use the backend Custom Fields module.

Next steps

Widgets

Render a component in a slot on an existing page.

Create a new page

Add a route with file-based routing and register it in the sidebar.