changes.json Reference
changes.json is where all entity customization lives. It sits alongside data.json in each version folder and is never overwritten by the generator — only merged when creating a new version.
Full Schema
{
"listingFields": {
"all": [],
"selected": ["id", "name", "status"],
"hideSelected": ["slug"],
"hide": ["internalId"],
"order": ["name", "status", "id"],
"sortable": ["name", "createdAt"],
"sortableKeys": { "status": "status.name" },
"columnWidths": { "name": 200 },
"alignment": { "price": "right", "status": "center" },
"defaultOrder": { "createdAt": "desc" },
"iconFields": ["status"]
},
"formFields": {
"sections": {
"General": ["name", "description", "type"],
"Pricing": ["price", "priceSpecial"],
"System": ["createdAt", "updatedAt"]
},
"required": ["name", "type"],
"optional": ["description"],
"hide": ["slug"],
"order": [],
"validation": {
"name": { "minLength": 2, "maxLength": 100 }
},
"defaultValues": { "status": "active" },
"helperText": { "price": "Enter price excluding tax" }
},
"formModalShowFields": {
"fields": ["name", "status", "createdAt"],
"systemFields": ["createdAt", "updatedAt", "id"],
"hide": [],
"validation": {},
"defaultValues": {},
"helperText": {}
},
"formModalEditFields": {
"fields": ["name", "status", "type"],
"hide": [],
"validation": { "name": { "minLength": 2 } },
"defaultValues": {},
"helperText": {}
},
"formModalAddFields": {
"fields": ["name", "status", "type"],
"hide": [],
"validation": {},
"defaultValues": { "status": "active" },
"helperText": {}
},
"showFields": {
"sections": {
"Information": ["name", "description", "status"],
"System": ["id", "createdAt", "updatedAt"]
},
"hide": [],
"order": []
},
"filterFields": {
"all": ["name", "status", "createdAt"],
"defaults": ["status"],
"hide": [],
"order": [],
"range": { "createdAt": true, "price": true },
"rangeKeys": {
"price": { "min": "priceMin", "max": "priceMax" }
},
"translatable": ["status"],
"searchMethods": {
"name": ["partial", "start", "exact"]
},
"className": { "name": "filter-wide" },
"hideToolbar": ["id"],
"helperText": { "createdAt": "Filter by creation date" },
"fieldIcon": { "status": "circle" },
"defaultFilterValues": { "status": "active" }
},
"sideboxFields": {
"all": [
{
"fieldName": "tags",
"targetResource": "tags",
"targetEntity": "TagBundle/Tag"
}
],
"hide": []
},
"fieldOverrides": {
"price": { "type": "CURRENCY", "format": "money" },
"rate": { "type": "PERCENT" },
"tableName": { "optionsArrayFinite": "TableNames" },
"fieldType": { "optionsArrayFinite": "PropertyTypes" }
},
"exclusionList": {
"listing": ["password", "token", "secretKey"],
"forms": ["id", "createdAt", "updatedAt", "deletedAt"],
"show": ["password"]
},
"actions": {
"listing": ["quick-show", "show", "quick-edit", "edit", "delete"],
"showPage": ["edit", "print", "delete", "export"]
}
}Section Breakdown
listingFields — Table / Grid Columns
| Key | Type | Description |
|---|---|---|
all | string[] | All available columns (from API serialization group) |
selected | string[] | Columns shown by default |
hideSelected | string[] | Columns in selected that are hidden from user toggle |
hide | string[] | Columns completely removed (highest priority) |
order | string[] | Column display order |
sortable | string[] | Columns with sort arrows |
sortableKeys | object | Map display key → API sort key when they differ |
columnWidths | object | Fixed width per column in pixels |
alignment | object | left / center / right per column |
defaultOrder | object | Default sort direction per field, e.g. { "name": "asc" } |
iconFields | string[] | Columns that render an icon instead of raw text |
formFields — Create / Edit Form
| Key | Type | Description |
|---|---|---|
sections | object | Group fields into named sections |
required | string[] | Fields with required validation |
optional | string[] | Non-required fields to include |
hide | string[] | Fields removed from the form |
validation | object | Per-field rules: minLength, maxLength, pattern |
defaultValues | object | Pre-filled values on form open |
helperText | object | Helper text shown below each field |
formModalShowFields / formModalEditFields / formModalAddFields
Three independent modal types — each is a separate key in changes.json:
| Modal key | Purpose | Affected by writeableProperties? |
|---|---|---|
formModalShowFields | Read-only quick-view modal (from listing row) | No — always shows all specified fields |
formModalEditFields | Editable quick-edit modal (from listing row) | Yes — filtered to writeable fields |
formModalAddFields | Quick-add modal (from listing toolbar) | Yes — filtered to writeable fields |
Fallback: If a typed modal key is absent, the generator falls back to the legacy formModalFields key. If that is also absent, the modal defaults to the required/filter fields.
MANY_TO_MANY fields are automatically excluded from all modal and form field lists. They render as sidebox components instead.
Shared keys for each modal:
| Key | Type | Description |
|---|---|---|
fields | string[] | Fields shown in the modal |
systemFields | string[] | (Show modal only) Shown in a collapsible “System Information” block |
hide | string[] | Fields excluded from this modal |
validation | object | Per-field validation rules |
defaultValues | object | Pre-filled values |
helperText | object | Helper text below each field |
showFields — Detail Page
| Key | Type | Description |
|---|---|---|
sections | object | Named sections with field arrays: { "Info": ["name", "status"] } |
hide | string[] | Fields excluded from the detail page |
sequencable | string[] | Fields that render next/previous navigation links |
iconFields | string[] | Fields rendered as icons instead of text |
order | string[] | Field display order when no sections are defined |
"showFields": {
"sections": {
"Information": ["name", "description"],
"System": ["id", "createdAt", "updatedAt"]
},
"hide": [],
"sequencable": ["status"],
"iconFields": ["priority"]
}filterFields — Search / Filter Panel
| Key | Type | Description |
|---|---|---|
all | string[] | All available filter fields (pre-populated by generator) |
defaults | string[] | Filters shown and active by default |
hide | string[] | Filters completely removed |
range | object | Enable range filter: { "price": true, "createdAt": true } |
rangeKeys | object | Custom min/max API param names: { "price": { "min": "priceMin", "max": "priceMax" } } |
translatable | string[] | Filter values that are translated before sending to API |
searchMethods | object | Search strategies per field: { "name": ["partial", "start", "exact"] } |
className | object | Custom CSS class per filter field: { "name": "filter-wide" } |
fieldIcon | object | Icon shown next to filter: { "status": "circle" } |
hideToolbar | string[] | Filters whose toolbar element is hidden |
helperText | object | Helper text below each filter |
defaultFilterValues | object | Default filter values pre-applied on page load: { "status": "active" } |
sideboxFields — Sidebar Relationships
Relationship fields (MANY_TO_MANY, ONE_TO_MANY) are rendered as sidebox components on the detail page.
"sideboxFields": {
"all": [
{
"fieldName": "tags",
"targetResource": "tags",
"targetEntity": "TagBundle/Tag"
}
],
"hide": ["attachments"]
}| Key in each item | Description |
|---|---|
fieldName | The relation field name on this entity |
targetResource | API resource slug for the related entity |
targetEntity | Entity path for the related entity (used for navigation) |
fieldOverrides — Custom Field Types & Options
Override the auto-detected TSX field type or add optionsArrayFinite for a finite dropdown:
"fieldOverrides": {
"amount": { "type": "CURRENCY" },
"rate": { "type": "PERCENT" },
"notes": { "type": "TEXTAREA" },
"tableName": { "optionsArrayFinite": "TableNames" },
"fieldType": { "optionsArrayFinite": "PropertyTypes" },
"propertyType":{ "optionsArrayFinite": "PropertyTypes" }
}When optionsArrayFinite is set, the generator emits ColumnTypeEnum.SELECT with the raw identifier (e.g. TableNames) as the options source — referencing the imported constant in the generated index.tsx. See Custom Properties for the full implementation.
Overrides apply to allAvailableFields — a merged pool of every field from every source. If a field name is not found in the pool, the override creates a synthetic field entry.
exclusionList — Global Field Exclusions
Hide fields across an entire context without listing them in every section. Checked alongside each section’s own hide array — both are applied:
"exclusionList": {
"listing": ["password", "token"],
"forms": ["id", "slug", "createdAt", "updatedAt", "deletedAt"],
"show": ["password"]
}| Category | Applied to |
|---|---|
listing | listingFields, filterFields |
forms | formFields, all formModal* sections |
show | showFields |
actions — Available Actions
"actions": {
"listing": ["quick-show", "show", "quick-edit", "edit", "delete"],
"showPage": ["edit", "print", "delete", "export"],
"listingAllActions": ["show", "edit", "delete", "export"],
"kanbanListingAllActions": ["show", "edit", "delete"],
"treeviewListingAllActions":["show", "edit", "delete", "move-up", "move-down"]
}| Key | Description |
|---|---|
listing | Default row actions shown on the listing page |
showPage | Actions shown on the detail / show page |
listingAllActions | Full set of possible row actions (all-actions menu) |
kanbanListingAllActions | Actions available in kanban view |
treeviewListingAllActions | Actions available in treeview mode |
Priority rule: hide always wins. A field in both selected and hide will be hidden.
Field Type Auto-Detection
The generator maps API field types and name patterns to TSX input types automatically. Override any mapping with fieldOverrides.
| API Type / Format | Field Name Pattern | TSX Type |
|---|---|---|
MANY_TO_ONE, ONE_TO_ONE, MANY_TO_MANY | — | SELECT_ASYNC |
string | *Email* | EMAIL |
string | *Phone*, *Tel* | TEL |
string | *color*, *Color* | COLOR |
string | *icon*, *Icon* | ICON |
string, money format | *price*, *amount*, *total*, *cost* | CURRENCY |
decimal, float | *rate*, *percent* | PERCENT |
boolean | — | BOOLEAN |
integer, smallint | — | NUMBER |
decimal | — | DECIMAL |
float | — | FLOAT |
text | — | TEXTAREA |
date | — | DATE |
datetime | — | DATETIME |
json_document | — | JSON |
string (default) | — | TEXT |