ArrayField

Form field component to handle array type values.

The ArrayField helps you to create complex forms with ease. It uses React Hook Forms useFieldArray internally.

Import

  • ArrayField: The wrapper component that composes the default ArrayField functionality.
  • ArrayFieldContainer: The container component provides context and state management.
  • ArrayFieldRows: Render prop component, to get access to the internal fields state. Must be a child of ArrayFieldContainer.
  • ArrayFieldRowContainer: The row container component providers row context.
  • ArrayFieldRowFields: Add the name prefix to the fields and acts as a horizontal form layout by default.
  • ArrayFieldAddButton: The default add button.
  • ArrayFieldRemoveButton: The default remove button.
  • useArrayFieldContext: A hook that returns the ArrayField internal state.
  • useArrayFieldRowContext: A hook that returns the ArrayFieldRowContainer state.
  • useArrayFieldAddButton: A hook that can be used to create a custom add button.
  • useArrayFieldRemoveButton: A hook that can be used to create a custom remove button.
import {
ArrayField,
ArrayFieldContainer,
ArrayFieldRows,
ArrayFieldRowContainer,
ArrayFieldRowFields,
ArrayFieldAddButton,
ArrayFieldRemoveButton,
useArrayFieldContext,
useArrayFieldRowContext,
useArrayFieldAddButton,
useArrayFieldRemoveButton,
} from '@saas-ui/react'

Usage

Auto generated form

Simple array of string values.

function CreateTodoItem() {
const schema = {
title: {
label: 'Title',
rules: {
required: true,
},
},
todos: {
type: 'array',
min: 1,
items: {
type: 'object',
properties: {
todo: {
label: 'Todo',
},
},
},
},
}
return (
<Form
defaultValues={{
title: '',
todos: [],
}}
schema={schema}
onSubmit={() => null}
/>
)
}

Basic object array

Field names inside an ArrayField need to be prefixed with array-field-name.$. The $ will be replaced with the index of the array item.

import { SubmitButton } from '@saas-ui/react'
import { Form } from '@saas-ui/forms/yup'
import * as yup from 'yup'
const issueSchema = yup.object().shape({
title: yup.string().required().label('Title'),
})
const schema = issueSchema.concat(
yup.object().shape({
issues: yup.array().min(1).max(4).of(issueSchema),
})
)
export default function CreateIssue() {
return (
<Form
schema={schema}
defaultValues={{
title: '',
issues: [{ title: '' }],
}}
onSubmit={() => Promise.resolve()}
>
{({ Field, ArrayField }) => (
<FormLayout>
<Field name="title" label="Title" />
<ArrayField name="issues" label="Sub-issues">
<Field name="issues.$.title" placeholder="Sub-issue title" />
</ArrayField>
<SubmitButton>Create todo</SubmitButton>
</FormLayout>
)}
</Form>
)
}

Complex array field

The array field can be fully composed to fit your specific use case.

import { SubmitButton } from '@saas-ui/react'
import { Form } from '@saas-ui/forms/yup'
import * as yup from 'yup'
const usersSchema = yup.object().shape({
id: yup.string().required().label('Id'),
name: yup.string().required().label('Name'),
lastName: yup.string().required().label('Last name'),
})
const schema = yup.object().shape({
users: yup.array().min(1).max(4).of(usersSchema),
})
export default function CustomArrayField() {
return (
<Form
schema={schema}
defaultValues={{
users: [
{
id: '123',
name: 'Eelco',
lastName: 'Wiersma',
},
],
}}
onSubmit={() => Promise.resolve()}
>
{({ Field }) => (
<FormLayout>
<ArrayFieldContainer
name="users"
label="Users"
defaultValue={{}}
keyName="key"
min={2}
max={4}
>
<ArrayFieldRows>
{(fields) => (
<>
{fields.map((field, i) => {
return (
<ArrayFieldRowContainer key={field.key} index={i}>
<ArrayFieldRowFields columns={3} spacing={1}>
<Field name="users.$.id" placeholder="Id" />
<Field name="users.$.name" placeholder="Name" />
<Box>
<Field
name={`users.${i}.lastName`}
placeholder="Last name"
/>
</Box>
</ArrayFieldRowFields>
<ArrayFieldRemoveButton />
</ArrayFieldRowContainer>
)
})}
</>
)}
</ArrayFieldRows>
<ArrayFieldAddButton />
</ArrayFieldContainer>
<SubmitButton label="Submit" />
</FormLayout>
)}
</Form>
)
}

Custom buttons

Use the useArrayFieldContext and useArrayFieldRowContext hooks to interact with the ArrayFieldContainer and create advanced custom components.

The useArrayFieldAddButton and useArrayFieldRemoveButton hooks can be used to create custom buttons.

import { Button } from '@saas-ui/react'
import {
useArrayFieldContext,
useArrayFieldAddButton,
useArrayFieldRemoveButton,
} from '@saas-ui/react'
const SimpleAddButton = () => {
return <Button {...useArrayFieldAddButton()}>Add record</Button>
}
const CustomAddButton = () => {
const { append, defaultValue, max, fields } = useArrayFieldContext()
const isDisabled = !!(max && fields.length >= max)
return (
<Button
onClick={() =>
append(defaultValue, {
shouldFocus: true,
focusName: `arrayField.${fields.length}.id`,
})
}
isDisabled={isDisabled}
>
Add record
</Button>
)
}
const RemoveButton = () => {
return (
<Button variant="ghost" {...useArrayFieldRemoveButton()}>
Remove
</Button>
)
}

Accessing the ArrayField context

You can access the ArrayField context by using a ref, or using useArrayFieldContext

import { Button } from '@chakra-ui/react'
import { SubmitButton } from '@saas-ui/react'
import { Form } from '@saas-ui/forms/yup'
import * as yup from 'yup'
const issueSchema = yup.object().shape({
title: yup.string().required().label('Title'),
})
const schema = issueSchema.concat(
yup.object().shape({
issues: yup.array().min(1).max(4).of(issueSchema),
})
)
export default function CreateIssue() {
const ref = React.useRef(null)
return (
<Form
defaultValues={{
title: '',
issues: [{ title: '' }],
}}
onSubmit={() => Promise.resolve()}
>
{({ Field, ArrayField }) => (
<FormLayout>
<Field name="title" label="Title" />
<ArrayField name="issues" label="Sub-issues" ref={ref}>
<Field name="issues.$.title" placeholder="Sub-issue title" />
</ArrayField>
<Button
onClick={() =>
ref.current.append({ title: 'Appended using the ref api' })
}
>
Add todo
</Button>
<SubmitButton>Create todo</SubmitButton>
</FormLayout>
)}
</Form>
)
}

Was this helpful?