Components and utilities for managing dynamic arrays of form fields with helper methods for manipulation.
Component for managing dynamic arrays of form fields with comprehensive manipulation methods.
/**
* Component for managing dynamic arrays of fields
* @param props - FieldArray configuration
* @returns JSX element with array manipulation capabilities
*/
function FieldArray(props: FieldArrayConfig): JSX.Element;
interface FieldArrayConfig {
/** Name of the array field in form values */
name: string;
/** Children render function */
children?: (props: FieldArrayRenderProps) => React.ReactNode;
/** Component to render */
component?: React.ComponentType<FieldArrayRenderProps>;
/** Render prop function */
render?: (props: FieldArrayRenderProps) => React.ReactNode;
}
interface FieldArrayRenderProps extends ArrayHelpers {
/** Current form object */
form: FormikProps<any>;
/** Field name */
name: string;
/** Helper functions for array manipulation */
push: (obj: any) => void;
swap: (indexA: number, indexB: number) => void;
move: (from: number, to: number) => void;
insert: (index: number, value: any) => void;
unshift: (value: any) => number;
remove: (index: number) => any | undefined;
pop: () => any | undefined;
replace: (index: number, value: any) => void;
}Usage Examples:
import { Formik, Form, Field, FieldArray } from "formik";
// Basic array management
const TodoList = () => (
<Formik
initialValues={{ todos: [''] }}
onSubmit={values => console.log(values)}
>
<Form>
<FieldArray name="todos">
{({ insert, remove, push }) => (
<div>
<button
type="button"
onClick={() => push('')}
>
Add Todo
</button>
{values.todos.length > 0 &&
values.todos.map((todo, index) => (
<div key={index}>
<Field name={`todos.${index}`} />
<button
type="button"
onClick={() => remove(index)}
>
Remove
</button>
</div>
))}
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
</Formik>
);
// Complex nested objects
const ContactForm = () => (
<Formik
initialValues={{
contacts: [{ name: '', email: '', phone: '' }]
}}
onSubmit={values => console.log(values)}
>
{({ values }) => (
<Form>
<FieldArray name="contacts">
{({ push, remove }) => (
<div>
{values.contacts.map((contact, index) => (
<div key={index}>
<Field name={`contacts.${index}.name`} placeholder="Name" />
<Field name={`contacts.${index}.email`} placeholder="Email" />
<Field name={`contacts.${index}.phone`} placeholder="Phone" />
<button type="button" onClick={() => remove(index)}>
Remove Contact
</button>
</div>
))}
<button
type="button"
onClick={() => push({ name: '', email: '', phone: '' })}
>
Add Contact
</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
// With validation
const ValidatedList = () => (
<Formik
initialValues={{ items: [''] }}
validate={values => {
const errors = {};
if (!values.items || values.items.length === 0) {
errors.items = 'At least one item is required';
} else {
const itemErrors = [];
values.items.forEach((item, index) => {
if (!item) {
itemErrors[index] = 'Item is required';
} else if (item.length < 3) {
itemErrors[index] = 'Item must be at least 3 characters';
}
});
if (itemErrors.length) {
errors.items = itemErrors;
}
}
return errors;
}}
onSubmit={values => console.log(values)}
>
{({ values, errors, touched }) => (
<Form>
<FieldArray name="items">
{({ push, remove }) => (
<div>
{values.items.map((item, index) => (
<div key={index}>
<Field name={`items.${index}`} />
{errors.items && errors.items[index] && touched.items && touched.items[index] && (
<div>{errors.items[index]}</div>
)}
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => push('')}>
Add Item
</button>
</div>
)}
</FieldArray>
{typeof errors.items === 'string' && <div>{errors.items}</div>}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);Helper methods available for array field manipulation.
interface ArrayHelpers<T = any> {
/** Add item to end of array */
push: (obj: T) => void;
/** Remove and return last item from array */
pop: () => T | undefined;
/** Remove and return item at index */
remove: (index: number) => T | undefined;
/** Insert item at specific index */
insert: (index: number, value: T) => void;
/** Add item to beginning of array */
unshift: (value: T) => number;
/** Move item from one index to another */
move: (from: number, to: number) => void;
/** Swap items at two indices */
swap: (indexA: number, indexB: number) => void;
/** Replace item at index with new value */
replace: (index: number, value: T) => void;
}Standalone utility functions for array manipulation that can be used outside of FieldArray context.
/**
* Move array element from one index to another
* @param array - Source array to modify
* @param from - Source index
* @param to - Destination index
* @returns New array with element moved
*/
function move<T>(array: T[], from: number, to: number): T[];
/**
* Swap two elements in an array
* @param array - Source array to modify
* @param indexA - First element index
* @param indexB - Second element index
* @returns New array with elements swapped
*/
function swap<T>(array: T[], indexA: number, indexB: number): T[];
/**
* Insert element at specific index
* @param array - Source array to modify
* @param index - Index to insert at
* @param value - Value to insert
* @returns New array with element inserted
*/
function insert<T>(array: T[], index: number, value: T): T[];
/**
* Replace element at specific index
* @param array - Source array to modify
* @param index - Index to replace at
* @param value - New value
* @returns New array with element replaced
*/
function replace<T>(array: T[], index: number, value: T): T[];Usage Examples:
import { move, swap, insert, replace } from "formik";
// Standalone usage outside of Formik
const items = ['a', 'b', 'c', 'd'];
// Move element from index 1 to index 3
const moved = move(items, 1, 3); // ['a', 'c', 'd', 'b']
// Swap elements at indices 0 and 2
const swapped = swap(items, 0, 2); // ['c', 'b', 'a', 'd']
// Insert element at index 2
const inserted = insert(items, 2, 'x'); // ['a', 'b', 'x', 'c', 'd']
// Replace element at index 1
const replaced = replace(items, 1, 'y'); // ['a', 'y', 'c', 'd']
// Using with useFormik
const ListManager = () => {
const formik = useFormik({
initialValues: { items: ['item1', 'item2', 'item3'] },
onSubmit: values => console.log(values),
});
const handleMoveUp = (index) => {
if (index > 0) {
const newItems = move(formik.values.items, index, index - 1);
formik.setFieldValue('items', newItems);
}
};
const handleMoveDown = (index) => {
if (index < formik.values.items.length - 1) {
const newItems = move(formik.values.items, index, index + 1);
formik.setFieldValue('items', newItems);
}
};
return (
<form onSubmit={formik.handleSubmit}>
{formik.values.items.map((item, index) => (
<div key={index}>
<input
name={`items.${index}`}
value={item}
onChange={formik.handleChange}
/>
<button type="button" onClick={() => handleMoveUp(index)}>
↑
</button>
<button type="button" onClick={() => handleMoveDown(index)}>
↓
</button>
</div>
))}
<button type="submit">Submit</button>
</form>
);
};Complex array manipulation patterns and nested array handling.
Nested Arrays:
const NestedArrayForm = () => (
<Formik
initialValues={{
sections: [
{
title: '',
items: ['']
}
]
}}
onSubmit={values => console.log(values)}
>
{({ values }) => (
<Form>
<FieldArray name="sections">
{({ push: pushSection, remove: removeSection }) => (
<div>
{values.sections.map((section, sectionIndex) => (
<div key={sectionIndex}>
<Field name={`sections.${sectionIndex}.title`} placeholder="Section Title" />
<FieldArray name={`sections.${sectionIndex}.items`}>
{({ push: pushItem, remove: removeItem }) => (
<div>
{section.items.map((item, itemIndex) => (
<div key={itemIndex}>
<Field name={`sections.${sectionIndex}.items.${itemIndex}`} />
<button type="button" onClick={() => removeItem(itemIndex)}>
Remove Item
</button>
</div>
))}
<button type="button" onClick={() => pushItem('')}>
Add Item
</button>
</div>
)}
</FieldArray>
<button type="button" onClick={() => removeSection(sectionIndex)}>
Remove Section
</button>
</div>
))}
<button type="button" onClick={() => pushSection({ title: '', items: [''] })}>
Add Section
</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);Conditional Array Items:
const ConditionalArrayForm = () => (
<Formik
initialValues={{
enableFeatures: false,
features: []
}}
onSubmit={values => console.log(values)}
>
{({ values, setFieldValue }) => (
<Form>
<Field type="checkbox" name="enableFeatures" />
<label>Enable Features</label>
{values.enableFeatures && (
<FieldArray name="features">
{({ push, remove }) => (
<div>
{values.features.map((feature, index) => (
<div key={index}>
<Field name={`features.${index}.name`} placeholder="Feature Name" />
<Field name={`features.${index}.enabled`} type="checkbox" />
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button
type="button"
onClick={() => push({ name: '', enabled: false })}
>
Add Feature
</button>
</div>
)}
</FieldArray>
)}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);