Managing Dynamic Arrays in Ant Design with Form.List
Learn how to use Ant Design's Form.List to handle dynamic arrays of input fields, ensuring correct state synchronization and validation for complex forms.
21 Jul 2025, 06:37 UTC

The Problem: Handling Unpredictable Input Counts
Standard form fields work well when you know exactly what data you need—one email, one password, one username. But real-world data is rarely that static. Whether you are building a user profile where a person can add multiple social media links, or an invoice system where a user adds an arbitrary number of line items, you need a way to manage arrays of inputs without manually syncing state between a React array and the form store.
The Form.List component in Ant Design solves this by treating a specific form field as an array, providing a render prop pattern that handles the addition, removal, and indexing of these dynamic fields automatically.
How Form.List Synchronizes State
Unlike a standard Form.Item, Form.List does not render an input itself. Instead, it provides a function as its child. This function receives a list of fields and a set of helper methods (like add and remove).
The critical engineering detail here is how Ant Design handles indices. When you remove the second item in a list of five, the form store doesn't just delete a key; it shifts the remaining items to maintain a contiguous array. This ensures that when the form is submitted, the resulting JSON is a clean array (e.g., [ {val: 'A'}, {val: 'B'} ]) rather than an object with fragmented keys.
Practical Implementation: Dynamic User Skills
To implement a dynamic list, you must wrap the Form.List inside a Form component. The name prop of the Form.List defines the key in the final submission object that will hold the array.
import { Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
const DynamicSkillsForm = () => {
return (
<Form onFinish={(values) => console.log('Submitted:', values)}>
<Form.List name="skills">
{(fields, { add, remove }) => (
<div>
{fields.map(({ key, name, ...restField }) => (
<Space key={key} style={{ display: 'flex', marginBottom: 8 }}>
<Form.Item
{...restField}
name={[name, 'skillName']}
rules={[{ required: true, message: 'Please enter a skill' }]}
>
<Input placeholder="e.g. React, TypeScript" />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Space>
))}
<Button type="dashed" onClick={() => add()} icon=<PlusOutlined /> Add Skill />
</div>
)}
</Form.List>
<Button type="primary" htmlType="submit">Submit</Button>
</Form>
);
};
Key Technical Details in the Example:
- The Name Array: Notice
name={[name, 'skillName']}. BecauseForm.Listhandles an array, the internalForm.Itemneeds a path.namerepresents the current index, and'skillName'is the key for the object at that index. - The Key Prop: We use
key={key}provided by thefieldsmap. Using the array index as a key can cause focus loss or state misalignment when items are deleted from the middle of the list. - Helper Methods: The
addfunction creates a new empty entry in the form store, whileremove(name)targets the specific index for deletion.
Performance and Constraints
While Form.List is powerful, it has a specific performance characteristic: the entire list typically re-renders when any single field within that list changes. For most business forms (5–20 items), this is negligible. However, if you are building a massive data-entry grid with hundreds of rows, you may notice input lag.
Additionally, Form.List is strictly tied to the Ant Design Form context. You cannot use it as a standalone component; it requires the parent Form to manage the underlying store and validation logic.
Verification and Testing
To verify your implementation is working correctly, perform these three checks:
- Submission Structure: Submit the form and ensure the output is an array of objects (e.g.,
{ skills: [{ skillName: 'React' }, { skillName: 'CSS' }] }) rather than a nested object. - Index Shifting: Add three items, then remove the first item. Ensure the second and third items shift up and that the
removefunction still targets the correct remaining items. - Validation: Add a
requiredrule to a field inside the list. Add a new row but leave it empty; the form should prevent submission and highlight the specific empty row.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.