Skip to content

Rent a car: States and conditional rendering

The form from the previous chapter renders every field unconditionally. We’ll now:

  1. Declare two statesdifferentReturn and hasDiscount — that mirror the matching toggles.
  2. Gate the optional fields with include: { in: [...] }.
  3. Add validators to every required field so the form’s health goes from erroredok only when all validators pass.

States are named boolean expressions evaluated against $form. Pass them as the second arg of the formConfig:

const formConfig = {
states: {
differentReturn: '$form.differentReturn === true',
hasDiscount: '$form.hasDiscountCode === true',
},
};

JSON forms put the same map at the form root under "states".

Add include: { in: ['<stateName>'] } to the optional widgets — they’ll only render while the named state is active.

02-states.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dropdown('car', {
labelField: 'label',
valueField: 'id',
items: [
{
id: 'compact',
label: 'Compact',
},
{
id: 'suv',
label: 'SUV',
},
{
id: 'convertible',
label: 'Convertible',
},
{
id: 'luxury',
label: 'Luxury',
},
],
label: 'Select car',
validator: {
type: 'string',
required: true,
},
}),
gui.inputs.dropdown('collectOffice', {
labelField: 'label',
valueField: 'id',
items: [
{
id: 'lhr',
label: 'London Heathrow',
},
{
id: 'cdg',
label: 'Paris CDG',
},
{
id: 'fra',
label: 'Frankfurt Main',
},
],
label: 'Collect from office',
validator: {
type: 'string',
required: true,
},
}),
gui.inputs.booleanInput('differentReturn', {
label: 'Choose a different return location',
}),
gui.inputs.dropdown('returnOffice', {
labelField: 'label',
valueField: 'id',
items: [
{
id: 'lhr',
label: 'London Heathrow',
},
{
id: 'cdg',
label: 'Paris CDG',
},
{
id: 'fra',
label: 'Frankfurt Main',
},
],
label: 'Return to office',
validator: {
type: 'string',
required: true,
},
}),
gui.inputs.rangeCalendar('rentalDates', {
label: 'Rental dates',
validator: {
type: 'array',
required: true,
minItems: 2,
maxItems: 2,
},
}),
gui.inputs.radiogroup('rentalType', {
options: [
{
label: 'Daily',
value: 'daily',
},
{
label: 'Weekly',
value: 'weekly',
},
{
label: 'Monthly',
value: 'monthly',
},
],
label: 'Rental type',
validator: {
type: 'string',
required: true,
},
}),
gui.inputs.booleanInput('driverOver25', {
label: 'Driver aged over 25',
validator: {
type: 'boolean',
const: true,
required: true,
},
}),
gui.inputs.booleanInput('hasDiscountCode', {
label: 'I have a discount code',
}),
gui.inputs.textInput('discountCode', {
label: 'Discount code',
validator: {
type: 'string',
required: true,
minLength: 4,
},
}),
gui.actions.button({
label: 'Reserve',
uid: 'submit',
onClick: 'submit',
}),
];
  • The Return to office dropdown only appears when Choose a different return location is on.
  • The Discount code input only appears when I have a discount code is on.

GolemUI emits a formHealth callback whenever the form’s validation status changes. You can wire it to a piece of UI state and conditionally disable your submit handler — or use the disabled prop on the button with a when expression that references $form.<paths>.

The example above uses the simpler 'submit' literal on the button: onClick: 'submit' triggers form-level validation before the event fires, so an invalid form won’t fire the event at all. See Submitting the form for the full submit wiring.

A custom item renderer →