Skip to content

Layouts

CommandForm renders children in the order they are defined. You can create custom layouts using standard HTML and CSS, or use the built-in CommandForm.Column component for multi-column layouts.

Fields remain bound at any descendant depth. You can place them inside fragments, intrinsic elements, grids, cards, or pass-through layout components without hoisting them to be direct children. The form preserves the layout tree while each field registers the accessor and population metadata it actually renders.

Each mounted field keeps one registration descriptor. Recreating its element or inline accessor during a layout render updates the descriptor’s callback references only after that render commits; it does not register or populate the field again, and an interrupted concurrent render cannot leak its callbacks into the mounted form. CommandForm automatically reacts when a layout changes the resolved property, currentValue, or noInitialValue. When an initialValue callback captures other semantics, pass that value through populationKey so a change recomputes the transformed value with the latest committed callback. A changed population source also uses the latest committed callback, including when both change in the same commit. This keeps layout and context renders stable without hiding intentional population changes.

Multi-Column Layout with CommandForm.Column

Section titled “Multi-Column Layout with CommandForm.Column”

CommandForm provides a built-in CommandForm.Column component for creating responsive multi-column layouts:

<CommandForm command={UpdateProfile}>
<h3>Personal Information</h3>
<CommandForm.Column>
<InputTextField<UpdateProfile> value={(c) => c.firstName} title='First Name' />
<InputTextField<UpdateProfile> value={(c) => c.lastName} title='Last Name' />
<InputTextField<UpdateProfile> value={(c) => c.phone} title='Phone' />
</CommandForm.Column>
<CommandForm.Column>
<InputTextField<UpdateProfile>
value={(c) => c.email}
type='email'
title='Email'
/>
<InputTextField<UpdateProfile> value={(c) => c.city} title='City' />
<InputTextField<UpdateProfile> value={(c) => c.country} title='Country' />
</CommandForm.Column>
<h3>Additional Details</h3>
<TextAreaField<UpdateProfile> value={(c) => c.bio} title='Biography' rows={5} />
</CommandForm>

The CommandForm.Column component:

  • Automatically arranges columns in a responsive layout
  • Displays side-by-side on wider screens
  • Stacks vertically on mobile devices
  • Maintains consistent spacing between fields
  • Works seamlessly with other CommandForm features (validation, errors, etc.)

Benefits:

  • Cleaner syntax than manual CSS Grid/Flexbox
  • Responsive by default
  • Consistent styling with the rest of CommandForm
  • Fields within columns behave identically to regular fields

Multi-Column Layout with CSS Grid (Alternative)

Section titled “Multi-Column Layout with CSS Grid (Alternative)”

For more control over layout, you can use CSS Grid directly:

<CommandForm command={UpdateProfile}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
<InputTextField<UpdateProfile> value={(c) => c.firstName} title='First Name' />
<InputTextField<UpdateProfile> value={(c) => c.lastName} title='Last Name' />
</div>
<InputTextField<UpdateProfile> value={(c) => c.email} type='email' title='Email' />
<TextAreaField<UpdateProfile> value={(c) => c.bio} title='Biography' rows={5} />
</CommandForm>

When to use CSS Grid:

  • You need precise control over column widths
  • You have complex, asymmetric layouts
  • You want to span fields across multiple columns

When to use CommandForm.Column:

  • Standard multi-column layouts
  • You want responsive behavior out of the box
  • You prefer cleaner, more semantic code
<CommandForm command={RegisterUser}>
<fieldset
style={{
border: '1px solid #e5e7eb',
borderRadius: '0.5rem',
padding: '1rem',
marginBottom: '1rem',
}}
>
<legend style={{ fontWeight: 'bold', padding: '0 0.5rem' }}>
Account Information
</legend>
<InputTextField<RegisterUser> value={(c) => c.username} title='Username' />
<InputTextField<RegisterUser> value={(c) => c.email} type='email' title='Email' />
<InputTextField<RegisterUser>
value={(c) => c.password}
type='password'
title='Password'
/>
</fieldset>
<fieldset
style={{ border: '1px solid #e5e7eb', borderRadius: '0.5rem', padding: '1rem' }}
>
<legend style={{ fontWeight: 'bold', padding: '0 0.5rem' }}>
Personal Information
</legend>
<InputTextField<RegisterUser> value={(c) => c.firstName} title='First Name' />
<InputTextField<RegisterUser> value={(c) => c.lastName} title='Last Name' />
<TextAreaField<RegisterUser> value={(c) => c.bio} title='Bio' rows={4} />
</fieldset>
</CommandForm>

Show fields based on other field values:

function RegistrationForm() {
const command = useCommandInstance(RegisterUser);
return (
<CommandForm command={RegisterUser}>
<InputTextField<RegisterUser>
value={(c) => c.email}
type='email'
title='Email'
/>
<SelectField<RegisterUser>
value={(c) => c.accountType}
title='Account Type'
options={[
{ id: 'personal', name: 'Personal' },
{ id: 'business', name: 'Business' },
]}
optionIdField='id'
optionLabelField='name'
/>
{command.accountType === 'business' && (
<>
<InputTextField<RegisterUser>
value={(c) => c.companyName}
title='Company Name'
/>
<InputTextField<RegisterUser> value={(c) => c.taxId} title='Tax ID' />
</>
)}
</CommandForm>
);
}