Skip to Content
UI KitAKI ComponentsAkiformBuilder

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-builder

Basic 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:

  1. Text
  2. Number
  3. Select
  4. Checkbox
  5. Date
  6. Textarea
  7. Field Array
  8. 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)
  • horizontal
  • inline

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:

  1. Synchronize form data with external state
  2. Implement complex form logic or validations
  3. 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-required is set on required fields
  • aria-invalid is 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

PropTypeDescription
fieldsFormField[]An array of field configurations for the form.
onSubmit(values: TFieldValues) => void | undefinedCallback 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.
showResetButtonbooleanWhether to show the reset button. Default is false.
onReset(event: React.FormEvent<HTMLFormElement>) => voidCallback function called when the form is reset.
controlledbooleanWhether the form should be in controlled mode. Default is false.
valuesTFieldValuesThe current values of the form fields (for controlled mode).
onValueChange(values: TFieldValues) => voidCallback function called when form values change.
initialValuesTFieldValuesInitial values for the form fields.
submitButtonPropsAkiformActionButtonType | undefinedProps for the submit button.
resetButtonPropsAkiformActionButtonType | undefinedProps for the reset button.
...formPropsOmit<AkiformProps, 'onChange' | 'labelCol' | 'wrapperCol'>Other form props from AkiformProps

FormField

PropertyTypeDescription
keystringUnique identifier for the field.
labelstring | undefinedLabel text for the field.
typeFieldTypeType of the field (e.g., ‘text’, ‘number’, ‘select’, etc.).
placeholderstringPlaceholder text for the field.
defaultValueanyDefault value for the field.
validationAnySchemaAkival validation schema for the field.
configFieldConfigAdditional configuration options for the field.
helpstring | undefinedHelp text for the field.
labelDescriptionstring | undefinedLabel description for the field.

FieldConfig

PropertyTypeDescription
visibleboolean | ((values: TFieldValues) => boolean)Whether the field should be visible.
disabledboolean | ((values: TFieldValues) => boolean)Whether the field should be disabled.

AkiformBuilderRef

MethodParametersDescription
reset(values?: Partial<TFieldValues>) => voidResets the form. If values is provided, it performs a partial reset.

SectionField

PropertyTypeDescription
keystringUnique identifier for the section.
labelstring | undefinedLabel text for the section.
type'section'Specifies that this is a section field.
fieldsFormField[]An array of fields to be included in this section.
defaultExpandedbooleanWhether the section should be expanded by default.

CustomField

PropertyTypeDescription
keystringUnique identifier for the custom field.
labelstring | undefinedLabel text for the custom field.
type'custom'Specifies that this is a custom field.
helpstring | undefinedHelp text for the custom field.
labelDescriptionstring | undefinedLabel description for the custom field.
render(props: { field: FormField; formValues: TFieldValues; control: Control<TFieldValues>; formState: FormState<TFieldValues> }) => React.ReactElementFunction to render the custom field.

RowField

PropertyTypeDescription
keystringUnique identifier for the row.
columnFieldsColumnField[]An array of column fields.
rowPropsRowPropsProps for the row.

ColumnField

PropertyTypeDescription
keystringUnique identifier for the column.
fieldsFormField[]An array of fields to be included in this column.
columnPropsColPropsProps for the column.

AkiformActionButtonType

PropertyTypeDescription
blockbooleanWhether the button should take up the full width of its container
...ButtonPropsOmit<IButtonProps, 'htmlType'>All other button props from IButtonProps except htmlType