Skip to content

Configuration

This page is the canonical reference for every input the form component accepts. The same surface ships in React, Angular, Lit, Vue, and Vanilla JS — only the binding syntax differs (prop={...} for React, [prop]="..." for Angular, .prop=${...} for Lit, :prop="..." for Vue, element.prop = ... for Vanilla JS). For the framework-typed signatures, see React, Angular, Lit, Vue, Vanilla JS.

You’re looking at the Programmatic view. The Programmatic API leans on formSelectors and formConfig to wire the engine — you’ll see those everywhere below. Switch to the JSON view from the language selector to see the same props described from the JSON form’s perspective.

A Programmatic array of gui.* shortcuts. Composed with gui.inputs.*, gui.layouts.*, gui.actions.*, gui.displays.* and resolved through the DX pipeline at render time.

import { gui } from '@golemui/gui-shared';
const formDef = [
gui.inputs.textInput('name', { label: 'Name' }),
gui.actions.button({ label: 'Submit', actionType: 'submit' }),
];

Wire it to the form component:

const config = { formDef };
// <GuiForm config={config} />

See Form Definition API.

The Programmatic API has two dedicated wiring objects — formSelectors for behavior decorators, and formConfig for engine knobs. Both are typed and composable.

GSL selector decorators that apply by widget type, tag, or uid. Built with gui.selectors.* and resolved against your formDef array at render time. See Selectors.

import { gui } from '@golemui/gui-shared';
const formSelectors = [
gui.selectors.inputs({ suppressAutomaticPlaceholders: true }),
gui.selectors.tag('identity').inputs({ autocomplete: 'off' }),
];

Wire it alongside formDef:

const config = { formDef, formSelectors };
// <GuiForm config={config} />

DX engine wiring container. Holds widgetLoaders, itemRenderers, dependencies, functions, validateOn, customValidators, states, suppressAutomaticStack. The Programmatic API treats formConfig as the single home for all of these knobs.

const formConfig = {
widgetLoaders: {
productCard: async () => (await import('./ProductCard')).ProductCard,
},
itemRenderers: { countryRenderer },
dependencies: { markdown: { parse: snarkdown } },
functions: {
grandTotal: (items = []) => items.reduce((sum, i) => sum + i.price, 0),
},
validateOn: 'eager',
states: {
hasDiscount: '$form.hasDiscountCode === true',
},
};

Wire it alongside formDef:

const config = { formDef, formConfig };
// <GuiForm config={config} />

A complete component skeleton wiring formDef, formSelectors, and formConfig together — copy this as the starting shape for your own form:

import { gui } from '@golemui/gui-shared';
import { GuiForm } from '@golemui/gui-react';
const formDef = [
gui.inputs.textInput('name', { label: 'Name' }),
gui.actions.button({ label: 'Submit', actionType: 'submit' }),
];
const formSelectors = [
gui.selectors.inputs({ suppressAutomaticPlaceholders: true }),
];
const formConfig = { validateOn: 'eager' as const };
const config = { formDef, formSelectors, formConfig };
export function MyForm() {
return <GuiForm config={config} />;
}

The initial form data object. Reads/writes flow through the engine’s reactive store; the value here only seeds the first render.

const formData = { username: 'golemui', email: 'hello@example.com' };
const config = { formDef, data: formData };
// <GuiForm config={config} />

Optional out-of-band metadata you want to thread through events and state. The engine doesn’t interpret it; you read it back from event.data / via custom widgets.

const formMeta = { tenantId: 'acme-co', sessionId: 'abc-123' };
const config = { formDef, data: formData, meta: formMeta };
// <GuiForm config={config} />

The next group of properties wires the engine — widget loaders, item renderers, custom validators, validation timing, dependencies, and states. They mean exactly the same thing in both paths; what differs is where you put them.

With the Programmatic API, the typical home for these knobs is formConfig — a typed object you nest inside the config prop. The top-level config properties (customWidgetLoaders, itemRenderers, etc.) also accept these knobs directly and merge with whatever you put in formConfig, which is handy when you want to keep environment-specific bits out of the resolved form.

Map of widget type → async loader. Merged with the framework’s default widget set.

Place it inside formConfig.widgetLoaders:

const formConfig = {
widgetLoaders: {
productCard: async () => (await import('./ProductCard')).ProductCard,
},
};
// <GuiForm config={config} ... />

See Features / Widget Loaders.

Map of renderer name → framework-specific component. Referenced from a widget’s itemRenderer prop (dropdown, list, radiogroup).

Place it inside formConfig.itemRenderers:

import { CountryRenderer } from './CountryRenderer';
const formConfig = {
itemRenderers: { countryRenderer: CountryRenderer },
};
// <GuiForm config={config} ... />

See Features / Item Renderers.

Map of validator name → schema factory. Referenced from a widget’s validator: { type: 'custom', <name>: <config> }. The factory follows the CustomValidatorSchemaFn interface and returns a schema (typically a Zod check):

import type { CustomValidatorSchemaFn } from '@golemui/core';
import { z } from 'zod';
export const allowedNames: CustomValidatorSchemaFn = (names: string[]) =>
z.string().check(
z.superRefine((val, ctx) => {
if (val && names.includes(val) === false) {
ctx.addIssue({
code: 'custom',
message: `Name "${val}" not in ${names.map((n) => `"${n}"`).join(', ')}`,
input: val,
});
}
}),
);

Place it inside formConfig.customValidators:

import { allowedNames } from './validators/allowed-names';
const formConfig = {
customValidators: { allowedNames },
};
// <GuiForm config={config} ... />

See Extending GolemUI / Validators.

When validation runs. Accepts a single mode or an array combining the latter three:

type ValidateOn =
| 'eager'
| 'change'
| 'blur'
| 'submit'
| ('change' | 'blur' | 'submit')[];
ModeTriggers when
'change'The user changes the field value
'blur'The user leaves the field
'submit'A submit event is emitted (all fields are touched first)
'eager'Any of the above happens (default)

Place it inside formConfig.validateOn:

const formConfig = { validateOn: 'change' };
// <GuiForm config={config} ... />

External helpers that widgets read at render time. Today, the only built-in widgets that need a dependency are Markdown and Markdown Text — both expect a markdown.parse function for live preview rendering.

Place it inside formConfig.dependencies:

import snarkdown from 'snarkdown';
const formConfig = {
dependencies: { markdown: { parse: snarkdown } },
};
// <GuiForm config={config} ... />

See Features / Dependencies.

Pure functions callable from reactive expressions under the $fn namespace — they must be synchronous and derive their result only from their arguments.

Place the map inside formConfig.functions:

const formConfig = {
functions: {
grandTotal: (items = []) => items.reduce((sum, i) => sum + (i.price ?? 0), 0),
},
};
// <GuiForm config={config} ... />

A functions entry passed directly on the config object is merged over formConfig.functions and wins on a name collision — that lets a shared form bundle ship defaults the host can override.

The map is captured when the form store is created, so it is not reactive: replacing it on a mounted form has no effect.

See Features / Host Functions.

Named boolean expressions evaluated against $form.

Place the map inside formConfig.states:

const formConfig = {
states: {
hasDiscount: '$form.hasDiscountCode === true',
},
};
// <GuiForm config={config} ... />

See Features / States.

The next four inputs apply equally to Programmatic and JSON forms. localization, middlewares, and formName sit inside the config object; autocomplete is a direct component attribute.

Translator implementing the I18nTranslator interface from @golemui/core. Resolves Localizable shapes ({ key, default, params }) on labels, placeholders, hints, validator messages, and section labels. Enables RTL automatically when the active language is RTL. See Features / i18n for translator implementations and the Responsive & RTL guide for RTL details.

Include localization in the config object alongside formDef and formConfig:

const config = { formDef, data: formData, formConfig, localization };
// <GuiForm config={config} />

Array of Middleware functions wrapping the form’s reactive store — used for logging, persistence, devtools. See Features / Middlewares.

import { devToolsMiddleware } from '@golemui/core';
const middlewares = [devToolsMiddleware()];
const config = { formDef, data: formData, middlewares };
// <GuiForm config={config} />

Sets the underlying <form> element’s autocomplete attribute. Useful to disable browser autofill across the entire form. Individual input widgets also accept an autocomplete prop (via props) for fine-grained control using any W3C autofill token (e.g. 'given-name', 'street-address', 'off').

const config = { formDef };
// <GuiForm config={config} autocomplete="off" />

Optional form identifier. Useful when you have several <gui-form> instances on the same page and want to disambiguate them in event payloads or middleware logs.

const config = { formDef, formName: 'checkout' };
// <GuiForm config={config} />

Fired when a widget fires a wired event (onClick, onChange, onLoad, onFilter, onBlur). Payload: { name, data, detail }. See Features / Form Events.

import type { FormEvent } from '@golemui/core';
function handleFormEvent(event: FormEvent) {
if (event.name === 'submit') {
console.log('Submit:', event.data);
}
}
const config = { formDef, data: formData };
// <GuiForm config={config} formEvent={handleFormEvent} />

Reports the form’s operational status whenever it changes. Payload: { status: 'ok' | 'errored', message?, code? }. An errored status covers both initialization problems (a malformed form definition, an undeclared state) and runtime problems (for example a string-interpolation syntax error); while errored, the form is considered non-operational. Wire this callback to disable a submit button, log the error, or trigger your own telemetry. To control what the user sees when the form is errored, use formHealthBoundary.

import type { FormHealth } from '@golemui/core';
function handleFormHealth(health: FormHealth) {
console.log('Health:', health.status);
}
const config = { formDef, data: formData };
// <GuiForm config={config} formHealth={handleFormHealth} />

When a form fails to initialize (a malformed definition, an undeclared state, a double-wrapped gui.* definition) the form would otherwise render as a silent blank. The formHealthBoundary controls what renders instead: it receives the current formHealth and, by default, prepends a red error banner above the form whenever the status is errored. When the status is ok it renders nothing extra, so a corrected definition restores the form in place.

Pass your own boundary to match your app’s design system or to log/escalate the error your own way. The form stays mounted inside the boundary, so recovering from an errored health clears your error UI automatically.

Each framework also exports its default boundary (DefaultFormHealthBoundary, defaultFormHealthBoundary, DefaultFormHealthBoundaryComponent) if you want to extend rather than replace it.

The contract differs slightly per framework:

React is a component receiving { health, children }; render children so the form stays mounted. Type: FormHealthBoundary from @golemui/react.

import type { FormHealthBoundary } from '@golemui/react';
const FormErrorBoundary: FormHealthBoundary = ({ health, children }) => (
<>
{health.status === 'errored' && (
<div role="alert" className="form-error">
<strong>This form could not be loaded</strong>
<div>{health.message}</div>
</div>
)}
{children}
</>
);
const config = { formDef, data: formData };
// <GuiForm config={config} formHealthBoundary={FormErrorBoundary} />