Framework: React Vue Svelte Solid
Group form functionality allows you to organize form fields into groups, making complex forms clearer and easier to manage. This chapter explains the configuration and usage of group forms.
Group Configuration
Group forms use the GroupSchema configuration to define groups. Each group contains a key, label, and field list.
Group Configuration Properties
GroupSchema Type
type GroupSchema = {
key : string ;
fields : FieldSchema [];
component ?: string ;
props ?: Record < string , unknown >;
};
Note : GroupSchema does not have a built-in label property. If you need to display a group title, pass it via props (e.g., props: { title: 'Group Title' }) and render it in your custom DefaultGroup component.
Property Details
Layout Configuration
By Group
Configure layout for specific groups:
const layout = {
groupClassName : 'form-group' ,
groupStyle : {
marginBottom : '24px' ,
padding : '20px' ,
border : '1px solid #e0e0e0' ,
borderRadius : '8px'
} ,
groups : {
personal : {
groupClassName : 'personal-group' ,
groupStyle : { backgroundColor : '#f0f8ff' }
} ,
contact : {
groupClassName : 'contact-group' ,
groupStyle : { backgroundColor : '#fff8e1' }
}
}
};
< Form schema = {schema} components = {components} layout = {layout} />
By Field
Configure layout for fields across groups:
const layout = {
groups : {
personal : {
fields : {
name : {
fieldClassName : 'name-field' ,
labelClassName : 'name-label'
} ,
age : {
fieldClassName : 'age-field'
}
}
}
}
};
Custom Group Container
Interface
A custom group container should accept the following props:
interface GroupComponentProps {
title ?: string ;
children : ReactNode ;
layout : object ;
className ?: string ;
style ?: CSSProperties ;
[key : string ] : any ;
}
Example: Collapsible Group
import { useState } from 'react' ;
const CollapsibleGroup = ({ title , children , className , style }) => {
const [ collapsed , setCollapsed ] = useState ( false );
return (
< div className = {className} style = {style}>
< div
style = {{
display : 'flex' ,
justifyContent : 'space-between' ,
alignItems : 'center' ,
cursor : 'pointer' ,
padding : '12px' ,
backgroundColor : '#f5f5f5' ,
borderRadius : '4px'
}}
onClick = {() => setCollapsed ( ! collapsed)}
>
< h3 style = {{ margin : 0 }}>{title}</ h3 >
< span >{collapsed ? '▼' : '▲' }</ span >
</ div >
{ ! collapsed && (
< div style = {{ padding : '16px' }}>
{children}
</ div >
)}
</ div >
);
};
Registering the Container
Register custom group containers via components, then reference them by name in GroupSchema.component:
const components = {
DefaultControl : InputComponent ,
CollapsibleGroup : CollapsibleGroup // Register custom group component
};
// Reference registered component by name in schema
const schema = [
{
key : 'basic' ,
component : 'CollapsibleGroup' , // Reference by name
props : { title : 'Basic Information' } ,
fields : [
{ type : 'string' , name : 'name' , label : 'Name' }
]
}
];
< Form schema = {schema} components = {components} />
Note : GroupSchema.component is used to reference a registered component by name, not to register it. All custom components must be registered via the components object.
Group Order
Groups are rendered in array order:
const schema : GroupSchema [] = [
{ key : 'first' , props : { title : 'First Group' } , fields : [ ... ] } , // Displayed first
{ key : 'second' , props : { title : 'Second Group' } , fields : [ ... ] } , // Displayed second
{ key : 'third' , props : { title : 'Third Group' } , fields : [ ... ] } // Displayed third
];
Schema Configuration Rules
Schema FormX has the following rules for schema configuration:
FieldSchema[] (field array) and GroupSchema[] (group array) cannot be mixed
If the schema contains items with a name property, it is treated as FieldSchema[] and automatically wrapped in a default group
If the schema contains items with a fields property, it is treated as GroupSchema[] and rendered by groups
// ✅ Correct: Pure field array (auto-wrapped in default group)
const fieldSchema : FieldSchema [] = [
{ type : 'string' , name : 'name' , label : 'Name' } ,
{ type : 'string' , name : 'email' , label : 'Email' }
];
// ✅ Correct: Pure group array
const groupSchema : GroupSchema [] = [
{
key : 'basic' ,
props : { title : 'Basic Information' } ,
fields : [
{ type : 'string' , name : 'name' , label : 'Name' }
]
}
];
// ❌ Wrong: Mixing fields and groups
const mixedSchema = [
{ type : 'string' , name : 'name' , label : 'Name' } , // Field
{ key : 'details' , fields : [ ... ] } // Group
];
Group Validation
Fields within groups are validated independently, using the same validation rules as standalone fields:
const schema : GroupSchema [] = [
{
key : 'basic' ,
props : { title : 'Basic Information' } ,
fields : [
{
type : 'string' ,
name : 'name' ,
label : 'Name' ,
required : true // Required validation
} ,
{
type : 'string' ,
name : 'email' ,
label : 'Email' ,
required : true ,
validator : ({ value }) => {
if ( ! value .includes ( '@' )) return 'Please enter a valid email' ;
}
}
]
}
];
Note : Schema FormX does not have built-in group-level validation. All validation is field-level. To implement group-level visual feedback (e.g., red border on error), use a custom group container combined with the errors object returned by validate().
Dynamic Groups
Conditional Field Display
GroupSchema does not have a hidden property. To conditionally show fields within a group, use field-level hidden with dependency linkage:
const schema : GroupSchema [] = [
{
key : 'userType' ,
props : { title : 'User Type' } ,
fields : [
{
type : 'string' ,
name : 'userType' ,
label : 'User Type' ,
props : { options : [ 'Individual' , 'Enterprise' ] }
}
]
} ,
{
key : 'personal' ,
props : { title : 'Personal Information' } ,
fields : [
{ type : 'string' , name : 'name' , label : 'Name' } ,
{
type : 'string' ,
name : 'company' ,
label : 'Company Name' ,
hidden : true , // Hidden by default
deps : [ 'userType' ] ,
onDepsChange : ({ deps , schema }) => ({
schema : { ... schema , hidden : deps[ 0 ] !== 'Enterprise' }
})
}
]
}
];
Best Practice : Use field-level hidden + deps + onDepsChange for conditional display, rather than dynamically modifying the schema structure. This approach is more stable and handles data cleanup correctly.
Common Use Cases
Use groups to implement multi-step forms:
const schema : GroupSchema [] = [
{
key : 'step1' ,
props : { title : 'Step 1: Basic Information' } ,
fields : [
{ type : 'string' , name : 'name' , label : 'Name' } ,
{ type : 'string' , name : 'email' , label : 'Email' }
]
} ,
{
key : 'step2' ,
props : { title : 'Step 2: Detailed Information' } ,
fields : [
{ type : 'string' , name : 'address' , label : 'Address' } ,
{ type : 'string' , name : 'phone' , label : 'Phone' }
]
}
];
Group related fields together:
const schema : GroupSchema [] = [
{
key : 'personal' ,
props : { title : 'Personal Information' } ,
fields : [
{ type : 'string' , name : 'name' , label : 'Name' } ,
{ type : 'number' , name : 'age' , label : 'Age' }
]
} ,
{
key : 'contact' ,
props : { title : 'Contact Information' } ,
fields : [
{ type : 'string' , name : 'email' , label : 'Email' } ,
{ type : 'string' , name : 'phone' , label : 'Phone' }
]
}
];
Best Practices
Group Design
Use groups to organize related fields
Keep groups focused on related functionality
Use clear, descriptive group labels
Layout
Provide adequate spacing between groups
Use consistent styling across groups
Customize styles for specific groups as needed
Avoid excessive nesting
Memoize custom group components
Handle empty groups gracefully