Custom Widgets
Each shortcut group exposes a .custom() factory for widgets you’ve implemented yourself. The shape mirrors the built-in shortcuts but takes the widget type as the first argument.
Per-group factories
Section titled “Per-group factories”import { gui } from '@golemui/gui-shared';
// Custom layoutgui.layouts.custom( 'productCard', [ /* children */ ], { title: 'My Product' },);
// Custom inputgui.inputs.custom('productRating', 'product.rating', { maxRating: 10, validator: { type: 'number', required: true },});
// Custom displaygui.displays.custom('productDescription', { img: 'assets/product.png', description: '### Cool product',});
// Custom actiongui.actions.custom('productShare', { onClick: 'shareEvent' });The first arg is the widget type string — it must match the key you register in formConfig.widgetLoaders.
Validators on custom inputs
Section titled “Validators on custom inputs”Custom inputs accept a validator prop typed as the union of all built-in validator types. Use { type: 'string'/'number'/'boolean'/'array'/'custom', ... }.
What this looks like in practice
Section titled “What this looks like in practice”Custom widgets really shine when you compose them. The Product Card below — built throughout the Extending GolemUI tutorial — combines all four kinds in a single form: a custom layout (productCard) wrapping a custom display (productDescription), a custom input (productRating), and a custom action (productShare). It also mixes in built-in widgets (textarea, button) without any special wiring.
Here’s the full form definition that produces the demo:
import { gui } from '@golemui/gui-shared';
const formDef = [ gui.layouts.custom( 'productCard', [ gui.displays.custom('productDescription', { img: 'https://picsum.photos/seed/golemui/240/240', description: '### GolemUI\nThe declarative form engine.', }), gui.inputs.custom('productRating', 'product.rating', { label: 'Your rating', maxRating: 10, validator: { type: 'number', required: true, messages: { required: 'Please rate the product before submitting' }, }, }), gui.inputs.textarea('product.comment', { label: 'Your comment', hint: 'Enter your comment (maximum 500 characters)', placeholder: 'Let us know why you love our product', counterMode: 'current', validator: { required: true, maxLength: 500, messages: { required: 'Please leave a comment with your rating', maxLength: 'Your comment is too long — keep it under 500 characters', }, }, }), gui.actions.custom('productShare', { label: 'Share', networks: [ { id: 'twitter', label: 'X', icon: 'https://cdn.simpleicons.org/x/333', }, { id: 'facebook', label: 'Facebook', icon: 'https://cdn.simpleicons.org/facebook/1877F2', }, { id: 'reddit', label: 'Reddit', icon: 'https://cdn.simpleicons.org/reddit/FF4500', }, ], onClick: 'shareEvent', }), gui.actions.button({ label: 'Submit', icon: 'save', iconPosition: 'right', actionType: 'submit', }), ], { title: 'Rate Our Product' }, ),];{ "kind": "layout", "type": "productCard", "props": { "title": "Rate Our Product" }, "children": [ { "kind": "display", "type": "productDescription", "props": { "img": "https://picsum.photos/seed/golemui/240/240", "description": "### GolemUI\nThe declarative form engine." } }, { "kind": "input", "type": "productRating", "path": "product.rating", "label": "Your rating", "props": { "maxRating": 10 }, "validator": { "type": "number", "required": true, "messages": { "required": "Please rate the product before submitting" } } }, { "kind": "input", "type": "textarea", "path": "product.comment", "label": "Your comment", "props": { "hint": "Enter your comment (maximum 500 characters)", "placeholder": "Let us know why you love our product", "counterMode": "current" }, "validator": { "type": "string", "required": true, "maxLength": 500, "messages": { "required": "Please leave a comment with your rating", "maxLength": "Your comment is too long — keep it under 500 characters" } } }, { "kind": "action", "type": "productShare", "label": "Share", "props": { "networks": [ { "id": "twitter", "label": "X", "icon": "https://cdn.simpleicons.org/x/333" }, { "id": "facebook", "label": "Facebook", "icon": "https://cdn.simpleicons.org/facebook/1877F2" }, { "id": "reddit", "label": "Reddit", "icon": "https://cdn.simpleicons.org/reddit/FF4500" } ] }, "on": { "click": "shareEvent" } }, { "kind": "action", "type": "button", "label": "Submit", "props": { "icon": "save", "iconPosition": "right" }, "on": { "click": "submit" } } ]}And the result — fully interactive:
The full per-framework implementation — React hooks, Angular adapters, Lit elements, plus how to register and event-handle each kind — lives in Extending GolemUI / Widgets Overview and the four kind-specific chapters under it.
See also
Section titled “See also”- Extending GolemUI / Widgets — full walkthrough of building each widget kind.
- Features / Widget Loaders — registering the loader.