States
States are named boolean expressions evaluated against the live form data. They let you describe complex conditional behavior once and reuse it across many widgets — without scattering inline conditionals throughout your form definition.
A state is a key-value pair: the key is the name you reference from include / exclude rules, prop overrides, and event handlers; the value is an expression string that reads from $form.<path>.
const states = { hasDiscount: '$form.hasDiscountCode === true', isAdult: '$form.driverAge >= 18',};{ "states": { "hasDiscount": "$form.hasDiscountCode === true", "isAdult": "$form.driverAge >= 18" }, "form": [ /* ... */ ]}Declaring states
Section titled “Declaring states”In the Programmatic API, states live inside formConfig.states. The form component reads them through formConfig, so the engine sees the same $form data the form itself binds to.
import { gui } from '@golemui/gui-shared';import { GuiForm } from '@golemui/gui-react';
const config = { formDef: [ gui.inputs.booleanInput('hasDiscountCode', { label: 'I have a discount code' }), gui.inputs.numberInput('driverAge', { label: 'Driver age' }), gui.inputs.textInput('discountCode', { label: 'Discount code', include: { in: ['hasDiscount'] }, }), ], formConfig: { states: { hasDiscount: '$form.hasDiscountCode === true', isAdult: '$form.driverAge >= 18', }, },};
export function MyForm() { return <GuiForm config={config} />;}import { Component } from '@angular/core';import { CommonModule } from '@angular/common';import { gui } from '@golemui/gui-shared';import { FormComponent } from '@golemui/gui-angular';
@Component({ imports: [CommonModule, FormComponent], selector: 'app-my-form', template: `<gui-form [config]="config"></gui-form>`,})export class MyForm { protected config = { formDef: [ gui.inputs.booleanInput('hasDiscountCode', { label: 'I have a discount code' }), gui.inputs.numberInput('driverAge', { label: 'Driver age' }), gui.inputs.textInput('discountCode', { label: 'Discount code', include: { in: ['hasDiscount'] }, }), ], formConfig: { states: { hasDiscount: '$form.hasDiscountCode === true', isAdult: '$form.driverAge >= 18', }, }, };}import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import { gui } from '@golemui/gui-shared';import '@golemui/gui-lit';
@customElement('my-form')export class MyForm extends LitElement { config = { formDef: [ gui.inputs.booleanInput('hasDiscountCode', { label: 'I have a discount code' }), gui.inputs.numberInput('driverAge', { label: 'Driver age' }), gui.inputs.textInput('discountCode', { label: 'Discount code', include: { in: ['hasDiscount'] }, }), ], formConfig: { states: { hasDiscount: '$form.hasDiscountCode === true', isAdult: '$form.driverAge >= 18', }, }, };
override createRenderRoot() { return this; }
override render() { return html`<gui-form .config=${this.config}></gui-form>`; }}<script setup lang="ts">import { gui } from '@golemui/gui-shared';import { GuiForm } from '@golemui/gui-vue';
const config = { formDef: [ gui.inputs.booleanInput('hasDiscountCode', { label: 'I have a discount code' }), gui.inputs.numberInput('driverAge', { label: 'Driver age' }), gui.inputs.textInput('discountCode', { label: 'Discount code', include: { in: ['hasDiscount'] }, }), ], formConfig: { states: { hasDiscount: '$form.hasDiscountCode === true', isAdult: '$form.driverAge >= 18', }, },};</script>
<template> <GuiForm :config="config" /></template>import '@golemui/gui-components/index.css';import '@golemui/gui-lit';import { gui } from '@golemui/gui-shared';
const form = document.getElementById('app-form');form.config = { formDef: [ gui.inputs.booleanInput('hasDiscountCode', { label: 'I have a discount code' }), gui.inputs.numberInput('driverAge', { label: 'Driver age' }), gui.inputs.textInput('discountCode', { label: 'Discount code', include: { in: ['hasDiscount'] }, }), ], formConfig: { states: { hasDiscount: '$form.hasDiscountCode === true', isAdult: '$form.driverAge >= 18', }, },};In the JSON path, states are declared inside the form payload itself under the top-level "states" key. There is no formConfig wrapper — the engine reads "states" directly off the parsed JSON.
{ "states": { "hasDiscount": "$form.hasDiscountCode === true", "isAdult": "$form.driverAge >= 18" }, "form": [ { "kind": "input", "type": "checkbox", "path": "hasDiscountCode", "label": "I have a discount code" }, { "kind": "input", "type": "number", "path": "driverAge", "label": "Driver age" }, { "kind": "input", "type": "textinput", "path": "discountCode", "label": "Discount code", "include": { "in": ["hasDiscount"] } } ]}Wire the parsed JSON into your form component the same way as any other JSON formDef — no extra prop is needed for states:
import { GuiForm } from '@golemui/gui-react';import formDef from './my-form.json';
export function MyForm() { return <GuiForm config={{ formDef }} />;}import { Component } from '@angular/core';import { CommonModule } from '@angular/common';import { FormComponent } from '@golemui/gui-angular';import formDef from './my-form.json';
@Component({ imports: [CommonModule, FormComponent], selector: 'app-my-form', template: `<gui-form [config]="config"></gui-form>`,})export class MyForm { protected config = { formDef };}import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-lit';import formDef from './my-form.json';
@customElement('my-form')export class MyForm extends LitElement { config = { formDef };
override createRenderRoot() { return this; }
override render() { return html`<gui-form .config=${this.config}></gui-form>`; }}<script setup lang="ts">import { GuiForm } from '@golemui/gui-vue';import formDef from './my-form.json';
const config = { formDef };</script>
<template> <GuiForm :config="config" /></template>import '@golemui/gui-components/index.css';import '@golemui/gui-lit';import formDef from './my-form.json';
const form = document.getElementById('app-form');form.config = { formDef };What you can do with a state name
Section titled “What you can do with a state name”Once a state is named, you reference it in three places:
include/exclude— gate whether a widget renders.- Per-state prop overrides — change a widget’s label, disabled flag, items list, etc. when the state is active.
- Inline
when— a one-shot expression that doesn’t get a name.
See the sub-pages for each pattern.
In this section
Section titled “In this section”- Include & Exclude — gate which widgets render.
- Properties — override widget props per state.
- Inline
when— ad-hoc conditionals without naming a state. - Composing states — combine multiple state expressions.