Skip to content

ArrayInput

To edit arrays of data embedded inside a record, <ArrayInput> creates a list of sub-forms.

ArrayInput

<ArrayInput> allows editing of embedded arrays, like the items field in the following order record:

{
"id": 1,
"date": "2022-08-30",
"customer": "John Doe",
"items": [
{
"name": "Office Jeans",
"price": 45.99,
"quantity": 1,
},
{
"name": "Black Elegance Jeans",
"price": 69.99,
"quantity": 2,
},
{
"name": "Slim Fit Jeans",
"price": 55.99,
"quantity": 1,
},
],
}

<ArrayInput> expects a single child, which must be a form iterator component. A form iterator is a component rendering a field array (the object returned by react-hook-form’s useFieldArray). For instance, the <SimpleFormIterator> component displays an array of Inputs in an unordered list (<ul>), one sub-form by list item (<li>). It also provides controls for adding and removing a sub-record.

import {
Edit,
SimpleForm,
TextInput,
ArrayInput,
NumberInput,
SimpleFormIterator
} from '@/components/admin';
const OrderEdit = () => (
<Edit>
<SimpleForm>
<TextInput source="customer" />
<TextInput source="date" type="date" />
<ArrayInput source="items">
<SimpleFormIterator inline>
<TextInput source="name" />
<NumberInput source="price" />
<NumberInput source="quantity" />
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Edit>
);

<SimpleFormIterator> will transform the source of its child Input components into nested inputs (items.0.name). Check the <SimpleFormIterator> documentation below for details about how to customize the sub form layout.

PropRequiredTypeDefaultDescription
sourceRequiredstring-Array field name
childrenRequiredReactNode-Iterator component(s)
classNameOptionalstring-Classes
defaultValueOptionalany[][]Initial array
helperTextOptionalReactNode-Help text
isPendingOptionalboolean-Show skeleton
labelOptionalstringInferredLabel text
validateOptionalValidator | Validator[]-Array-level validation

Iterator for repeating groups inside <ArrayInput>. Renders each item with reorder, remove, and add controls.

PropRequiredTypeDefaultDescription
addButtonOptionalReactElementDefaultCustom add button
classNameOptionalstring-Wrapper classes
disableAddOptionalbooleanfalseHide add button
disableClearOptionalbooleanfalseHide clear-all button
disableRemoveOptionalboolean | (record)=>booleanfalseDisable remove globally or per record
disableReorderingOptionalbooleanfalseHide move up/down
getItemLabelOptionalboolean | (index)=>string|ReactElementfalseAdd per-item label
inlineOptionalbooleanfalseArrange child inputs horizontally (responsive)
removeButtonOptionalReactElementDefaultCustom remove button
reOrderButtonsOptionalReactElementDefaultCustom reorder buttons

If you are using an <ArrayInput> inside a form with global validation, you need to shape the errors object returned by the validate function like an array too.

{
authors: [
{},
{
name: 'A name is required',
role: 'ra.validation.required' // translation keys are supported too
},
],
}

<ArrayInput> does not support the disabled and readOnly props.

If you need to disable the input, set the <SimpleFormIterator disabled> prop, and make the child inputs readOnly:

const OrderEdit = () => (
<Edit>
<SimpleForm>
<TextInput source="customer" />
<DateInput source="date" />
<ArrayInput source="items">
<SimpleFormIterator inline disabled>
<TextInput source="name" readOnly/>
<NumberInput source="price" readOnly />
<NumberInput source="quantity" readOnly />
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Edit>
);

Changing An Item’s Value Programmatically

Section titled “Changing An Item’s Value Programmatically”

You can leverage react-hook-form’s setValue method to change an item’s value programmatically.

However you need to know the name under which the input was registered in the form, and this name is dynamically generated depending on the index of the item in the array.

To get the name of the input for a given index, you can leverage the SourceContext created by ra-core, which can be accessed using the useSourceContext hook.

This context provides a getSource function that returns the effective source for an input in the current context, which you can use as input name for setValue.

Here is an example where we leverage getSource and setValue to change the role of an user to ‘admin’ when the ‘Make Admin’ button is clicked:

import { ArrayInput, SimpleFormIterator, TextInput } from '@/components/admin';
import { Button } from '@/components/ui/button';
import { useSourceContext } from 'ra-core';
import { useFormContext } from 'react-hook-form';
const ChangeRoleButton = () => {
const sourceContext = useSourceContext();
const { setValue } = useFormContext();
const onClick = () => {
// sourceContext.getSource('role') will for instance return
// 'users.0.role'
setValue(sourceContext.getSource('role'), 'admin');
};
return (
<Button onClick={onClick} size="small" sx={{ minWidth: 120 }}>
Make admin
</Button>
);
};
const UserArray = () => (
<ArrayInput source="users">
<SimpleFormIterator inline>
<TextInput source="name" helperText={false} />
<TextInput source="role" helperText={false} />
<ChangeRoleButton />
</SimpleFormIterator>
</ArrayInput>
);