widget_menu — field types
widget_menu inside the widget JSON defines the form editors fill in via the Pagebuilder properties panel. Each entry describes exactly one field — from a single-line text to sub-item repeaters. This page lists every field type with its schema and real examples.
Base structure
Every field is a JSON object with at least row and type:
{
"row": "title",
"type": "text",
"placeholder": "Title"
}| Field | Required | Purpose |
|---|---|---|
row | yes (except items) | DB column in page_row_col_widget — see Available DB columns |
type | yes | Field type — see the types below |
placeholder | no | Label in the properties panel |
select_value | for select (DB) | {table, value, title, options: []} |
options | for select (static) | [{id, title}, ...] |
menu | for items | Sub-menu definition for repeater items |
Field type overview
| Type | Renders as | Data target |
|---|---|---|
text | Single-line input | page_row_col_widget.{row} |
textarea | Multi-line input | page_row_col_widget.{row} |
filemanager | Media picker button | page_row_col_widget.{row} (JSON with media ID) |
select (static) | Dropdown with inline options | page_row_col_widget.{row} |
select (DB) | Dropdown from a DB table | page_row_col_widget.{row} |
items | Sub-items repeater | page_widget_item table |
On save, form values are written to this widget's page_row_col_widget row. Items land in a separate table (see Widget items / repeater).
text — single-line input
For titles, links, slugs, short labels.
{"row": "title", "type": "text", "placeholder": "Title"}Access in the Vue template: widget.data?.title. Access in getWidgetContent($widget_array): $widget_array['widget_title'].
row: "text" uses the key widget_html
When a field has "row": "text" (e.g. for an HTML block), the key in $widget_array is widget_html, not widget_text. The remap happens in PageService.php (the key name makes the HTML-content semantic of the column explicit). Details: getWidgetContent() › widget_array keys.
textarea — multi-line input
For longer texts, simple HTML blocks, short descriptions.
{"row": "text", "type": "textarea", "placeholder": "Text / HTML"}Access: widget.data?.text in the Vue template, $widget_array['widget_html'] in PHP.
textarea does not ship a rich-text editor. For CKEditor fields inside a Pagebuilder widget form, there is currently no official field-type mapping — widgets either use textarea with HTML freedom, or load CKEditor per page, not per field.
filemanager — image/media
Opens the Media Manager as a modal. The editor picks a file; the widget value is stored as the JSON [{"id":"123"}].
{"row": "image", "type": "filemanager", "placeholder": "Image"}Resolution to the full URL happens in getWidgetContent() via files::loadURL():
$current_item['image'] = files::loadURL($dsatz_news['image']);The Vue template then receives a ready-to-use URL at widget.content.items[0].image. If you want to render images directly from widget.data.image, you have to parse it yourself.
select (static)
Dropdown with hard-coded options. Example from the Title widget (widgetsBase/bootstrap.php):
{
"row": "subtitle",
"type": "select",
"placeholder": "Type",
"value": "id",
"title": "title",
"options": [
{"id": "0", "title": "H1"},
{"id": "1", "title": "H2"},
{"id": "2", "title": "H3"}
]
}| Field | Purpose |
|---|---|
options | Inline array with options |
value | Key of the options object that gets stored |
title | Key shown in the dropdown |
Legacy format
The static select variant uses value/title fields directly at the field level — without a select_value wrapper. That's the legacy format, but the widget form still supports it and it's convenient for inline options. For DB selects, use the new variant (see below).
select (DB-backed)
Dropdown with options from a DB table. The editor sees the title values and the value key (e.g. id) gets stored.
{
"row": "subtitle",
"type": "select",
"placeholder": "Product",
"select_value": {
"table": "s_products",
"value": "id",
"title": "title",
"options": []
}
}select_value field | Purpose |
|---|---|
table | DB table to read from |
value | Column stored as the value |
title | Column shown in the dropdown |
options | Must be empty ([]) — populated by the backend at render time |
select_value is required for DB selects
The old format with table/value/title directly at the field level (without a select_value wrapper) is still supported for backwards compatibility but is deprecated. For new widgets, always use select_value.
Inside getWidgetContent(), the selected value arrives as an ID:
$productId = (int) $widget_array['widget_subtitle'];
$q = query("SELECT title, price FROM s_products WHERE id = $productId LIMIT 1");items — repeater
For lists of similar sub-elements (FAQ entries, USP icons, testimonials, gallery images). Items live in the separate page_widget_item table with the base_id/language_short pattern.
{
"type": "items",
"placeholder": "Testimonials",
"menu": [
{"row": "title", "type": "text", "placeholder": "Name"},
{"row": "subtitle", "type": "text", "placeholder": "Position"},
{"row": "text", "type": "textarea", "placeholder": "Quote"},
{"row": "image", "type": "filemanager", "placeholder": "Avatar"}
]
}items field | Purpose |
|---|---|
type | Always "items" |
placeholder | Label of the sub-list in the properties panel |
menu | Array of field types — describes the fields per item |
items has no row — items go into page_widget_item, not into a column of page_row_col_widget. Details: Widget items / repeater.
Available DB columns
row must match the name of a column in page_row_col_widget exactly. The full list:
row | Type | Commonly used for |
|---|---|---|
title | tinytext | Widget title, main text |
subtitle | tinytext | Subtitle, select value, type |
text | mediumtext | HTML/text content (PHP key: widget_html!) |
link | tinytext | URL, link |
image | tinytext | Image (filemanager JSON) |
gallery | tinytext | Gallery data |
logo | tinytext | Logo image |
color | tinytext | Color |
price | tinytext | Price |
sale_status | tinytext | Sale status |
location | tinytext | Location |
vcard | tinytext | vCard data |
video | tinytext | Video URL |
short_text | tinytext | Short text |
map | tinytext | Map data |
accordion | tinytext | Accordion data |
sliderrevolution | tinytext | Slider-Revolution ID |
pagebuilder | tinytext | Pagebuilder page ID |
form | tinytext | Form ID |
row with an unknown column
If you write "row": "myCustomField", the value is discarded on save — the column does not exist in page_row_col_widget. The editor sees the field in the properties panel, but the entered value vanishes on the next page load. Never add new columns to page_row_col_widget — for structured extra data, repurpose one of the existing slots semantically (e.g. subtitle for a second text block) or use an items repeater.
image2, pname, dateStart, dateEnd exist only in page_widget_item
These columns exist only in the items table, not in page_row_col_widget. Using them in the top-level widget menu leads to the same silent drop behavior as above.
Complete widget menu — example
From the titleBanner widget or similarly structured banners:
{
"widget_title": "Title Banner",
"widget_icon": "icon.png",
"widget_subtitle": "Banner with title, subtitle and background image",
"widget_template": "titleBanner",
"widget_menu": [
{"row": "title", "type": "text", "placeholder": "Title"},
{"row": "subtitle", "type": "text", "placeholder": "Subtitle"},
{"row": "image", "type": "filemanager", "placeholder": "Background"},
{"row": "link", "type": "text", "placeholder": "Link URL"},
{"row": "short_text", "type": "textarea", "placeholder": "Button label"}
]
}Five fields, no items, no DB selects — a standard content block. More complex widgets (shop listing, e-learning, menu editor widget) add items plus getWidgetContent().
Common issues
Fields without row (except items)
Every field type except items needs a row. Without it, the form stores the value nowhere — the editor types, the field appears to work, and on reload the value is gone.
Mixing select_value with inline options
- Static options → put
options: [...]withvalue+titleat the field level (legacy format, still fine). - DB options → put
select_value: {table, value, title, options: []}as a wrapper. Combining both in parallel, or using a DB select withoutselect_value, will fail.
items.menu instead of items.widget_menu
The sub-field on items is called menu, not widget_menu. Confusing the two leads to an empty repeater.
type is case-sensitive
"type": "Text" is not recognized as text — field types are lowercase. Same for every other type.
See also
- Widget Anatomy — where
widget_menulives in the JSON - getWidgetContent() — how form values and dynamic data come together in PHP
- Widget items / repeater — the
itemsfield type in detail - Custom CSS convention —
.widget_{id}class per widget instance - Plugins › Buttons — the same field types in a plugin modal context