Organize fields into horizontal rows for compact layouts. Rows display fields side-by-side.
Interactive Demo
Basic Row
{
type: 'row',
fields: [
{ key: 'firstName', type: 'input', label: 'First Name', value: '', col: 6 },
{ key: 'lastName', type: 'input', label: 'Last Name', value: '', col: 6 },
],
}Rows flatten their children - they don't add nesting to form values. Use the col property to control field widths (see Column Sizing below).
Column Sizing
Control field widths within rows using the col property:
{
type: 'row',
fields: [
{ key: 'city', type: 'input', label: 'City', value: '', col: 6 },
{ key: 'state', type: 'select', label: 'State', value: '', col: 3 },
{ key: 'zip', type: 'input', label: 'ZIP', value: '', col: 3 },
],
}The col property uses a 12-column grid system (like Bootstrap). In this example:
citytakes 6/12 (50%) widthstatetakes 3/12 (25%) widthziptakes 3/12 (25%) width
Responsive Behavior
Rows automatically stack on small screens, making forms mobile-friendly without additional configuration.
Complete Example
Here's a complete working example of a row field with multiple fields:
import { Component } from '@angular/core';
import { DynamicForm } from '@ng-forge/dynamic-forms';
@Component({
selector: 'app-address-form',
imports: [DynamicForm],
template: `<form [dynamic-form]="formConfig"></form>
>`,
})
export class AddressFormComponent {
formConfig = {
fields: [
{
type: 'row',
fields: [
{
key: 'firstName',
type: 'input',
label: 'First Name',
value: '',
required: true,
col: 6,
},
{
key: 'lastName',
type: 'input',
label: 'Last Name',
value: '',
required: true,
col: 6,
},
],
},
{
key: 'email',
type: 'input',
label: 'Email Address',
value: '',
required: true,
email: true,
},
{
type: 'row',
fields: [
{
key: 'city',
type: 'input',
label: 'City',
value: '',
required: true,
col: 6,
},
{
key: 'state',
type: 'input',
label: 'State',
value: '',
required: true,
maxLength: 2,
col: 3,
},
{
key: 'zip',
type: 'input',
label: 'ZIP',
value: '',
required: true,
pattern: /^\d{5}$/,
col: 3,
},
],
},
],
};
}This produces a flat form value (rows don't create nesting):
{
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@example.com',
city: 'Springfield',
state: 'IL',
zip: '62701'
}Value Structure
Rows are layout containers - they don't add nesting to your form values. Fields flatten to the root level:
// Form config with rows
{
fields: [
{
type: 'row',
fields: [
{ key: 'firstName', type: 'input', value: '', col: 6 },
{ key: 'lastName', type: 'input', value: '', col: 6 },
],
},
];
}
// Resulting form value (flat structure)
{
firstName: 'John',
lastName: 'Doe',
// Note: row itself is NOT in the value
}Nesting Restrictions
Row fields can be used within:
- Pages (top-level container)
- Groups (for layouts within grouped data)
- Arrays (for layouts within array items)
Rows cannot be nested inside:
- Other row fields
Allowed Children
Rows can contain:
- Leaf fields (input, select, checkbox, etc.)
- Group fields (for nested data structures within the row)
- Array fields (for repeating sections within the row)
Conditional Visibility
Row containers support the logic property to conditionally show or hide the entire row (and all its fields) based on form state.
{
key: 'contactRow',
type: 'row',
logic: [{
type: 'hidden',
condition: {
type: 'fieldValue',
fieldPath: 'contactPreference',
operator: 'equals',
value: 'none',
},
}],
fields: [
{ key: 'phone', type: 'input', label: 'Phone', col: 6 },
{ key: 'email', type: 'input', label: 'Email', col: 6 },
],
}Only 'hidden' is supported as a logic type on containers. For all available condition types and operators, see Conditional Logic.
Next Steps
- Form Groups — Nest fields under a single key for structured form data
- Form Arrays — Create repeating sections with add/remove controls
- Form Pages — Build multi-step wizard forms with page navigation