| title | Plugin Form |
|---|
import { InteractiveDemo } from '@/app/components/InteractiveDemo'; import { PluginLoader } from '@/app/components/PluginLoader';
Advanced form components with validation, multi-step forms, and comprehensive field support.
npm install @object-ui/plugin-form<PluginLoader plugins={['form']}>
- Form Builder - Create complex forms from schemas
- Validation - Built-in validation with custom rules
- Multi-Step Forms - Wizard-style multi-step forms
- Field Types - All standard HTML5 input types
- Form State - Automatic state management
- Error Handling - Field-level and form-level errors
{
type: 'form',
fields: FormField[],
submitLabel?: string,
cancelLabel?: string,
onSubmit?: (data) => void,
onCancel?: () => void,
className?: string
}
interface FormField {
name: string;
type: string; // 'input', 'select', 'checkbox', etc.
label: string;
placeholder?: string;
required?: boolean;
validation?: ValidationRule[];
defaultValue?: any;
disabled?: boolean;
className?: string;
}
A sectioned form stays one form: declare the tabs on it and the renderer distributes the fields into panels, instead of rendering a form per section (which strands every section but the first outside the submit, and lets an inactive tab unmount along with its values).
{
type: 'form',
fields: [/* every tab's fields, in one flat list */],
fieldTabs: [
{ key: 'basics', label: 'Basics', fields: ['subject', 'status'] },
{ key: 'detail', label: 'Detail', description: 'Anything else', fields: ['description'] },
],
defaultFieldTab?: 'basics', // defaults to the first tab
fieldTabsPosition?: 'top', // 'top' | 'bottom' | 'left' | 'right'
}
- Panels are force-mounted and only CSS-hidden, so a tab the user leaves keeps its values and its validation.
- A failed submit activates the tab holding the first offending field and marks
every tab with a rejected field — client rules and server
fields[]alike. - Fields no tab claims render above the tab strip rather than disappearing.
- Needs at least two tabs; ignored when the form uses
children.
ModalForm (contentLayout: 'tabbed') and TabbedForm build on this.
Side-by-side panels follow the same rule: the <form> wraps the whole panel
group and each pane holds only fields, so one react-hook-form instance spans the
divider.
{
type: 'form',
fields: [/* every pane's fields, in one flat list */],
fieldPanes: [
{ key: 'primary', fields: ['subject'], defaultSize: 50 },
{ key: 'secondary', fields: ['status', 'priority'], defaultSize: 50, minSize: 20 },
],
fieldPanesOrientation?: 'horizontal', // 'horizontal' | 'vertical'
fieldPanesResizable?: true, // false pins the divider
}
- A submit from anywhere carries every pane's values, and a field rule in one pane can read a field in another — neither works with a form per panel.
defaultSize/minSizeare percentages of the group.- Each pane is its own
@container, so a multi-column group collapses as the divider is dragged narrower. - Fields no pane claims render above the panel group rather than disappearing.
- Needs at least two panes; ignored when the form uses
childrenorfieldTabs.
SplitForm builds on this: section 1 becomes the primary pane, the rest stack in
the secondary one behind inline section headers.
import '@object-ui/plugin-form';
import { formComponents } from '@object-ui/plugin-form';
import { ComponentRegistry } from '@object-ui/core';
Object.entries(formComponents).forEach(([type, component]) => {
ComponentRegistry.register(type, component);
});
{
"type": "form",
"fields": [
{
"name": "username",
"type": "input",
"label": "Username",
"required": true,
"validation": [
{ "type": "minLength", "value": 3, "message": "Min 3 characters" },
{ "type": "maxLength", "value": 20, "message": "Max 20 characters" }
]
},
{
"name": "password",
"type": "input",
"inputType": "password",
"label": "Password",
"required": true,
"validation": [
{ "type": "minLength", "value": 8, "message": "Min 8 characters" }
]
}
],
"submitLabel": "Sign Up"
}{
"type": "multi-step-form",
"steps": [
{
"title": "Personal Info",
"fields": [
{ "name": "firstName", "type": "input", "label": "First Name", "required": true },
{ "name": "lastName", "type": "input", "label": "Last Name", "required": true }
]
},
{
"title": "Contact Info",
"fields": [
{ "name": "email", "type": "input", "inputType": "email", "label": "Email", "required": true },
{ "name": "phone", "type": "input", "inputType": "tel", "label": "Phone" }
]
}
]
}The plugin supports these field types:
- input - Text, email, password, number, tel, url, etc.
- textarea - Multi-line text input
- select - Dropdown select
- checkbox - Single checkbox
- radio-group - Radio button group
- date-picker - Date selection
- file-upload - File upload
import type { FormSchema, FormField } from '@object-ui/plugin-form';
const loginForm: FormSchema = {
type: 'form',
fields: [...],
submitLabel: 'Sign In'
};
MIT