AkiformBuilder
AkiformBuilder is a powerful and flexible form generation tool for React
applications. It’s designed to make creating complex forms easier and more
efficient. This component is part of the @akinon/akiform-builder package and
works on top of Akiform, using Akival for schema validation.
Installation
To use AkiformBuilder in your project, you need to install it along with its peer dependencies:
pnpm add @akinon/akiform-builderBasic Usage
Here’s a simple example of how to use AkiformBuilder:
import React from 'react';
import { AkiformBuilder, field } from '@akinon/akiform-builder';
import { akival } from '@akinon/akival';
const MyForm = () => {
// Define the fields for the form
const fields = [
field()
.key('name')
.label('Name')
.type('text')
.placeholder('Enter your name')
.validation(akival.string().required('Name is required'))
.labelDescription('* marked fields are mandatory')
.help('Description text')
.build(),
field()
.key('email')
.label('Email')
.type('text')
.placeholder('Enter your email')
.validation(
akival.string().email('Invalid email').required('Email is required')
)
.help('Description text')
.build()
];
// Define the submit handler
const handleSubmit = values => {
console.log('Form submitted with values:', values);
};
return <AkiformBuilder fields={fields} onSubmit={handleSubmit} />;
};
export default MyForm;This example creates a simple form with two fields: name and email. It uses the
field() builder to create the field configurations and sets up basic
validation rules.
Field Types
AkiForm Builder supports various field types to cover different form input needs:
- Text
- Number
- Select
- Checkbox
- Date
- Textarea
- Field Array
- Custom
Let’s look at examples for each type:
Text Field
import { field } from '@akinon/akiform-builder';
const fields = [
field()
.key('name')
.label('Name')
.type('text')
.placeholder('Enter your name')
.validation(akival.string().required('Name is required'))
.help('Description text')
.labelDescription('* marked fields are mandatory')
.build()
];Number Field
import { field } from '@akinon/akiform-builder';
const fields = [
field()
.key('age')
.label('Age')
.type('number')
.placeholder('Enter your age')
.validation(akival.number().min(18, 'You must be at least 18 years old'))
.help('Description text')
.labelDescription('* marked fields are mandatory')
.build()
];Select Field
import { field } from '@akinon/akiform-builder';
const fields = [
field()
.key('gender')
.label('Gender')
.type('select')
.options(['Male', 'Female', 'Other'])
.validation(akival.string().required('Gender is required'))
.help('Description text')
.labelDescription('* marked fields are mandatory')
.build()
];Checkbox Field
import { field } from '@akinon/akiform-builder';
const fields = [
field()
.key('isActive')
.label('Active')
.type('checkbox')
.validation(akival.boolean().required('Active status is required'))
.help('Description text')
.labelDescription('* marked fields are mandatory')
.build()
];Date Field
import { field } from '@akinon/akiform-builder';
const fields = [
field()
.key('dob')
.label('Date of Birth')
.type('date')
.validation(akival.date().required('Date of Birth is required'))
.showTime(true)
.help('Description text')
.labelDescription('* marked fields are mandatory')
.build()
];Textarea Field
import { field } from '@akinon/akiform-builder';
const fields = [
field()
.key('bio')
.label('Bio')
.type('textarea')
.validation(akival.string().required('Bio is required'))
.help('Description text')
.labelDescription('* marked fields are mandatory')
.build()
];Field Array
Field arrays allow you to create dynamic form sections that can be repeated:
import { field } from '@akinon/akiform-builder';
const addressField = field()
.key('addresses')
.label('Addresses')
.type('fieldArray')
.build();
addressField.fields = [
field().key('street').label('Street').type('text').build(),
field().key('city').label('City').type('text').build(),
field().key('country').label('Country').type('text').build()
];Custom Field
You can also create custom field types to fit your specific requirements:
import { field } from '@akinon/akiform-builder';
const CustomComponent = ({ field, formValues, control, formState }) => {
// Your custom field implementation using formState
return <div>Custom field: {field.label}</div>;
};
const customField = field()
.key('custom')
.label('Custom Field')
.type('custom')
.help('Description text')
.labelDescription('* marked fields are mandatory')
.render(({ field, formValues, control, formState }) => (
<CustomComponent
field={field}
formValues={formValues}
control={control}
formState={formState}
/>
))
.build();This allows for more advanced custom field implementations that can react to the overall form state.
Section Field
AkiformBuilder supports collapsible form sections, allowing you to group related
fields together and create more organized, complex forms. You can create a
section using the field() builder with the 'section' type:
import { field } from '@akinon/akiform-builder';
const personalInfoSection = field()
.key('personalInfo')
.label('Personal Information')
.type('section')
.defaultExpanded(true)
.fields([
field().key('name').label('Name').type('text').build(),
field().key('email').label('Email').type('text').build()
])
.build();
const fields = [personalInfoSection, / other fields /];Sections can be expanded or collapsed by default using the defaultExpanded
property.
Row and Column Fields
AkiformBuilder supports row and column layouts to create complex form layouts.
Row fields must always contain columnFields, and columnFields must always
use column field type. Column fields can contain complex form structures,
including other row fields or any other field types, allowing for nested
layouts:
import { field } from '@akinon/akiform-builder';
const formFields = field()
.type('row')
.key('mainrow')
.columnFields([
field()
.type('column')
.key('col1')
.fields([
field()
.type('row')
.key('row1')
.columnFields([
field()
.type('column')
.key('username_col')
.fields([
field().type('text').key('username').label('Username').build()
])
.build()
])
.build(),
field()
.type('row')
.key('row2')
.columnFields([
field()
.type('column')
.key('name_col')
.fields([field().type('text').key('name').label('Name').build()])
.build(),
field()
.type('column')
.key('surname_col')
.fields([
field().type('text').key('surname').label('Surname').build()
])
.build()
])
.build()
])
.build()
])
.build();
const fields = [formFields /* other fields */];
/*
Layout:
+------------------------------------------+
| mainrow |
| +--------------------------------------+ |
| | col1 | |
| | +----------------------------------+ | |
| | | row1 | | |
| | | +------------------------------+ | | |
| | | | username_col | | | |
| | | | +------------------------+ | | | |
| | | | | Username | | | | |
| | | | +------------------------+ | | | |
| | | +------------------------------+ | | |
| | +----------------------------------+ | |
| | +----------------------------------+ | |
| | | row2 | | |
| | | +--------------+ +-------------+ | | |
| | | | name_col | | surname_col | | |
| | | | +----------+ | | +----------+ | | |
| | | | | Name | | | | Surname | | | |
| | | | +----------+ | | +----------+ | | |
| | | +--------------+ +-------------+ | | |
| | +----------------------------------+ | |
| +--------------------------------------+ |
+------------------------------------------+
*/Validation
AkiForm Builder uses Akival for form validation. You can add validation rules to your fields like this:
import { field } from '@akinon/akiform-builder';
const fields = [
field()
.key('email')
.label('Email')
.type('text')
.validation(
akival
.string()
.email('Invalid email format')
.required('Email is required')
)
.build()
];Form Layout
You can customize the form layout using the layout and layoutOptions props:
<AkiformBuilder
fields={fields}
onSubmit={handleSubmit}
layout="horizontal"
layoutOptions={{ labelCol: { span: 8 }, wrapperCol: { span: 16 } }}
/>Available layout options are:
vertical(default)horizontalinline
Controlled vs Uncontrolled Mode
AkiForm Builder supports both controlled and uncontrolled modes, offering flexibility in how you manage form state.
Uncontrolled Mode
In uncontrolled mode, AkiForm Builder manages its own internal state. This is the default behavior and is suitable for most scenarios where you don’t need to directly manipulate the form’s values from outside the component.
Example use case: A simple registration form where you only need to handle the final submitted data.
...
<AkiformBuilder fields={fields} onSubmit={handleSubmit} />Throttled Form Value Updates
When using AkiformBuilder in uncontrolled mode with the onValueChange prop,
form value updates are now throttled. This helps to improve performance by
reducing the frequency of updates, especially in forms with many fields or
complex validation logic.
The default throttle delay is 300ms.
Controlled Mode
In controlled mode, you manage the form’s state externally and pass it to AkiformBuilder. This mode is useful when you need to:
- Synchronize form data with external state
- Implement complex form logic or validations
- Pre-fill form data dynamically
To use controlled mode, set the controlled prop to true and provide values
and onValueChange props:
...
const [formValues, setFormValues] = useState({});
<AkiformBuilder
fields={fields}
onSubmit={handleSubmit}
controlled={true}
values={formValues}
onValueChange={setFormValues}
/>;Reset Functionality
You can show the default reset button to reset the form:
<AkiformBuilder
fields={fields}
onSubmit={handleSubmit}
showResetButton={true}
onReset={() => console.log('Form reset')}
/>You can also reset the form programmatically using a ref:
...
const formRef = useRef();
// Later in your code
formRef.current.reset();
// In your JSX
<AkiformBuilder ref={formRef} fields={fields} onSubmit={handleSubmit} />;Partial Form Reset
In addition to full form resets, you can now perform partial resets on specific fields:
const formRef = useRef<AkiformBuilderRef>();
// Later in your code
formRef.current?.reset({ fieldName: 'new value' });
// In your JSX
<AkiformBuilder ref={formRef} fields={fields} onSubmit={handleSubmit} />;This allows you to reset only certain fields while leaving others unchanged.
Custom Reset Button
You can customize the reset button’s appearance and behavior using
resetButtonProps:
<AkiformBuilder
fields={fields}
onSubmit={handleSubmit}
showResetButton={true}
onReset={() => console.log('Form reset')}
resetButtonProps={{
children: 'Reset Form',
block: true
}}
/>Conditional Rendering and Disabling Fields
You can conditionally render or disable fields based on form values:
import { field } from '@akinon/akiform-builder';
const fields = [
field().key('showExtra').label('Show extra field').type('checkbox').build(),
field()
.key('extraField')
.label('Extra Field')
.type('text')
.config({
visible: values => values.showExtra,
disabled: values => !values.showExtra
})
.build()
];Accessibility
AkiformBuilder includes additional ARIA attributes to enhance accessibility:
aria-requiredis set on required fieldsaria-invalidis set on fields with validation errors
These attributes help screen readers provide more context to users about form field requirements and validation states.
Internationalization
AkiformBuilder supports internationalization. You can customize the language used for form labels and error messages.
Advanced Usage
Here’s an example that combines multiple features:
import React, { useRef } from 'react';
import { AkiformBuilder, field } from '@akinon/akiform-builder';
import { akival } from '@akinon/akival';
const AdvancedForm = () => {
const formRef = useRef();
const fields = [
field()
.key('name')
.label('Name')
.type('text')
.validation(akival.string().required('Name is required'))
.help('Description text')
.labelDescription('* marked fields are mandatory')
.build(),
field()
.key('age')
.label('Age')
.type('number')
.validation(akival.number().min(18, 'Must be at least 18'))
.help('Description text')
.build(),
field()
.key('country')
.label('Country')
.type('select')
.options([
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'uk', label: 'United Kingdom' }
])
.help('Description text')
.build(),
field()
.key('bio')
.label('Biography')
.type('textarea')
.config({
visible: values => values.age >= 18
})
.help('Description text')
.build()
];
const handleSubmit = values => {
console.log('Form submitted:', values);
};
const handleReset = () => {
console.log('Form reset');
};
return (
<AkiformBuilder
ref={formRef}
fields={fields}
onSubmit={handleSubmit}
onReset={handleReset}
showResetButton={true}
layout="horizontal"
layoutOptions={{ labelCol: { span: 6 }, wrapperCol: { span: 18 } }}
/>
);
};
export default AdvancedForm;This example demonstrates:
- Multiple field types
- Validation
- Conditional rendering
- Custom layout
- Reset functionality
- Form ref for programmatic control
API Reference
AkiformBuilder Props
| Prop | Type | Description |
|---|---|---|
fields | FormField[] | An array of field configurations for the form. |
onSubmit | (values: TFieldValues) => void | undefined | Callback function called when the form is submitted. |
layout | 'vertical' | 'horizontal' | 'inline' | The layout of the form. Default is ‘vertical’. |
layoutOptions | { labelCol?: ColProps; wrapperCol?: ColProps } | Options for customizing the layout. |
showResetButton | boolean | Whether to show the reset button. Default is false. |
onReset | (event: React.FormEvent<HTMLFormElement>) => void | Callback function called when the form is reset. |
controlled | boolean | Whether the form should be in controlled mode. Default is false. |
values | TFieldValues | The current values of the form fields (for controlled mode). |
onValueChange | (values: TFieldValues) => void | Callback function called when form values change. |
initialValues | TFieldValues | Initial values for the form fields. |
submitButtonProps | AkiformActionButtonType | undefined | Props for the submit button. |
resetButtonProps | AkiformActionButtonType | undefined | Props for the reset button. |
...formProps | Omit<AkiformProps, 'onChange' | 'labelCol' | 'wrapperCol'> | Other form props from AkiformProps |
FormField
| Property | Type | Description |
|---|---|---|
key | string | Unique identifier for the field. |
label | string | undefined | Label text for the field. |
type | FieldType | Type of the field (e.g., ‘text’, ‘number’, ‘select’, etc.). |
placeholder | string | Placeholder text for the field. |
defaultValue | any | Default value for the field. |
validation | AnySchema | Akival validation schema for the field. |
config | FieldConfig | Additional configuration options for the field. |
help | string | undefined | Help text for the field. |
labelDescription | string | undefined | Label description for the field. |
FieldConfig
| Property | Type | Description |
|---|---|---|
visible | boolean | ((values: TFieldValues) => boolean) | Whether the field should be visible. |
disabled | boolean | ((values: TFieldValues) => boolean) | Whether the field should be disabled. |
AkiformBuilderRef
| Method | Parameters | Description |
|---|---|---|
reset | (values?: Partial<TFieldValues>) => void | Resets the form. If values is provided, it performs a partial reset. |
SectionField
| Property | Type | Description |
|---|---|---|
key | string | Unique identifier for the section. |
label | string | undefined | Label text for the section. |
type | 'section' | Specifies that this is a section field. |
fields | FormField[] | An array of fields to be included in this section. |
defaultExpanded | boolean | Whether the section should be expanded by default. |
CustomField
| Property | Type | Description |
|---|---|---|
key | string | Unique identifier for the custom field. |
label | string | undefined | Label text for the custom field. |
type | 'custom' | Specifies that this is a custom field. |
help | string | undefined | Help text for the custom field. |
labelDescription | string | undefined | Label description for the custom field. |
render | (props: { field: FormField; formValues: TFieldValues; control: Control<TFieldValues>; formState: FormState<TFieldValues> }) => React.ReactElement | Function to render the custom field. |
RowField
| Property | Type | Description |
|---|---|---|
key | string | Unique identifier for the row. |
columnFields | ColumnField[] | An array of column fields. |
rowProps | RowProps | Props for the row. |
ColumnField
| Property | Type | Description |
|---|---|---|
key | string | Unique identifier for the column. |
fields | FormField[] | An array of fields to be included in this column. |
columnProps | ColProps | Props for the column. |
AkiformActionButtonType
| Property | Type | Description |
|---|---|---|
block | boolean | Whether the button should take up the full width of its container |
...ButtonProps | Omit<IButtonProps, 'htmlType'> | All other button props from IButtonProps except htmlType |