Layout JSON Format
The structure of a custom object layout is defined using JSON. If no JSON is provided (empty), all properties are displayed in a simple vertical list.
This page is the reference for the JSON schema. For an overview of how layouts are managed and assigned to users, see Custom Object Layouts. For the list of built-in renderers and their options, see Layout Renderers Reference.
Node Types
The layout JSON consists of nested nodes. Each node must have a type property. The available node types are:
Container Nodes (can contain other nodes):
VerticalLayout- Arranges children vertically, one below anotherHorizontalLayout- Arranges children horizontally in columnsSection- A collapsible section with a header labelTabs- Organizes children as tabbed panels
Leaf Nodes (display content):
Field- Displays a property field. Optionally accepts arendererto swap the default input for a specialised widget (see Renderers).Content- Displays static HTML content (headings, text, separators, etc.)
Basic Structure
A simple vertical layout with three fields:
{
"type": "VerticalLayout",
"children": [
{"type": "Field", "fieldId": "Name"},
{"type": "Field", "fieldId": "Description"},
{"type": "Field", "fieldId": "Status"}
]
}
Field Node
Field nodes reference a property by name using the fieldId property:
{"type": "Field", "fieldId": "PropertyName"}
The fieldId must match the name of an active property definition for the custom object.
Fields not included in the layout will appear in an "Other Fields" section.
Content Node
Content nodes display static HTML content such as headings, help text, or separators.
Use the html property to specify the content:
{"type": "Content", "html": "<h4>Section Title</h4><p>Enter details below.</p>"}
Content nodes can also be used as spacers in horizontal layouts by using an empty html value:
{
"type": "HorizontalLayout",
"children": [
{"type": "Field", "fieldId": "FirstName", "columns": 5},
{"type": "Content", "html": "", "columns": 2},
{"type": "Field", "fieldId": "LastName", "columns": 5}
]
}
In a HorizontalLayout, Content nodes must include a columns property like other children.
Allowed HTML tags: b, div, hr, i, u, li, sup, br, ol, ul, font, small, h1, h2, h3, h4, p, a, span, sub, em, table, thead, tbody, th, tr, td.
For safety, HTML content is sanitized before display. Script tags and other potentially unsafe content will be removed.
Horizontal Layout
Use HorizontalLayout to arrange fields side by side. Each child must specify a columns value:
{
"type": "HorizontalLayout",
"children": [
{"type": "Field", "fieldId": "FirstName", "columns": 6},
{"type": "Field", "fieldId": "LastName", "columns": 6}
]
}
The total columns should not exceed 12. In this example, both fields take up half the width (6 + 6 = 12).
Section
Sections group related fields with a labeled header:
{
"type": "Section",
"label": "Contact Information",
"children": [
{"type": "Field", "fieldId": "Email"},
{"type": "Field", "fieldId": "Phone"}
]
}
Tabs
Tabs organize content into tabbed panels. Each child must have a label property for the tab title:
{
"type": "Tabs",
"children": [
{
"type": "VerticalLayout",
"label": "General",
"children": [
{"type": "Field", "fieldId": "Name"},
{"type": "Field", "fieldId": "Description"}
]
},
{
"type": "VerticalLayout",
"label": "Details",
"children": [
{"type": "Field", "fieldId": "Status"},
{"type": "Field", "fieldId": "Priority"}
]
}
]
}
Read-Only Fields
You can mark fields or entire containers as read-only by adding "read_only": true to a node.
Read-only fields are displayed with a lock icon and cannot be edited by the user, even if they
have edit permissions. This is useful for creating "review" layouts where most fields should be locked.
Making a single field read-only:
{"type": "Field", "fieldId": "ApprovedBy", "read_only": true}
Making all fields in a container read-only:
{
"type": "Section",
"label": "Approved Details",
"read_only": true,
"children": [
{"type": "Field", "fieldId": "ApprovedBy"},
{"type": "Field", "fieldId": "ApprovalDate"}
]
}
Overriding a parent's read-only setting:
A child node can explicitly set "read_only": false to override a parent container's read-only
setting. In this example, the "Status" field is editable even though the parent container is read-only:
{
"type": "VerticalLayout",
"read_only": true,
"children": [
{"type": "Field", "fieldId": "Name"},
{"type": "Field", "fieldId": "Description"},
{"type": "Field", "fieldId": "Status", "read_only": false}
]
}
The inheritance rules are:
If a node has
"read_only": true, it and all its descendants are read-only (unless overridden).If a node has
"read_only": false, it explicitly overrides any inherited read-only state.If a node does not have a
read_onlyproperty, it inherits from its parent.Content nodes do not support
read_only(they have no editable content).
In the layout editor preview, read-only fields are shown with a "Read-only" badge and a grey background.
Renderers
Each Field node in a layout normally uses the default input for the property's value type
(textarea for Text, dropdown for Choice, native picker for Date, and so on). To replace the default
with a specialised widget, set a renderer on the Field node:
{
"type": "Field",
"fieldId": "Quantity",
"renderer": "number-spinner"
}
With a renderer-specific override:
{
"type": "Field",
"fieldId": "Quantity",
"renderer": "number-spinner",
"rendererOptions": {"step": 0.5}
}
Renderer-related properties on a Field node:
renderer— the registered renderer name (e.g."number-spinner"). Optional; when omitted the field is rendered using the default for its value type.rendererOptions— an optional object of renderer-specific settings. Most renderers take their data from the underlying property definition (number-spinnerreads min/max/decimals from the numeric property;transfer-listreads values and separator from the Set property) and need norendererOptionsat all.
Most renderers restrict which property value types they support. number-spinner only works
with Numeric properties, radio-buttons only with Choice properties, and
transfer-list only with Set properties. Assigning a renderer to an incompatible field
shows a validation error when the layout is saved.
A Field with a renderer behaves identically to a plain Field for everything else (read-only inheritance, "Other Fields" detection, columns in HorizontalLayout, etc.) — the only difference is how the value is presented and edited.
For a full list of available renderers and their options, see the Layout Renderers Reference.
Note
Compound widgets that span several fields (for example, a row that displays role and
level together, or an editor that owns multiple related fields at once) are not yet supported.
When the need is concrete, those will arrive as a separate node type rather than as a
variation of Field — keeping single-field renderers and multi-field widgets cleanly
distinct in the schema.
Complex Example
This example combines multiple layout features including tabs, sections, horizontal layouts, content, and a renderer override:
{
"type": "Tabs",
"children": [
{
"type": "VerticalLayout",
"label": "Overview",
"children": [
{
"type": "Content",
"html": "<h4>Basic Information</h4><p>Enter the core details for this item.</p>"
},
{
"type": "HorizontalLayout",
"children": [
{"type": "Field", "fieldId": "Name", "columns": 8},
{"type": "Field", "fieldId": "Status", "columns": 4}
]
},
{"type": "Field", "fieldId": "Description"}
]
},
{
"type": "VerticalLayout",
"label": "Details",
"children": [
{
"type": "Section",
"label": "Dates",
"children": [
{
"type": "HorizontalLayout",
"children": [
{"type": "Field", "fieldId": "StartDate", "columns": 6},
{"type": "Field", "fieldId": "EndDate", "columns": 6}
]
}
]
},
{"type": "Content", "html": "<hr>"},
{
"type": "Section",
"label": "Assignment",
"children": [
{"type": "Field", "fieldId": "AssignedTo"},
{
"type": "Field",
"fieldId": "Priority",
"renderer": "number-spinner"
}
]
}
]
}
]
}
Validation Rules
The layout JSON is validated when saving:
The root must be a valid node type (typically
VerticalLayout,Section, orTabs).All
fieldIdvalues must match active property definition names.HorizontalLayoutchildren must havecolumnsvalues between 1 and 12.HorizontalLayouttotal columns should not exceed 12.Tabschildren must havelabelproperties.read_only, if present, must be a boolean (trueorfalse).Contentnodes do not supportread_only.If a Field's
rendereris set, it must name a registered renderer and the field's value type must be compatible with that renderer's allowed field types.rendererOptions, if present, must be an object whose keys match the renderer's options schema.