or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

context-integration.mdcore-components.mderror-handling.mdfield-arrays.mdform-state-management.mdindex.md
tile.json

field-arrays.mddocs/

Dynamic Field Arrays

Components and utilities for managing dynamic arrays of form fields with helper methods for manipulation.

Capabilities

FieldArray Component

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>
);

ArrayHelpers Interface

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;
}

Array Utility Functions

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>
  );
};

Advanced Array Patterns

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>
);