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?