Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/reatom/reatom/llms.txt

Use this file to discover all available pages before exploring further.

Overview

reatomFieldArray creates a reactive atom for managing dynamic arrays of form fields. It combines LinkedListAtom with field capabilities, providing:
  • Efficient list operations (add, remove, reorder)
  • Form state tracking (validation, focus, dirty)
  • Support for nested field structures
  • Factory functions for custom field creation

Type Signature

function reatomFieldArray<Param, Node extends FieldsAtomizeInitState>(
  initState: Param[] | ((param: Param, name: string) => Node),
  options?: FieldArrayOptions<Param, Node>
): FieldArrayAtom<Param, Node>

Parameters

initState
Param[] | CreateFunction
Either:
  • Array of initial values (automatically converted to fields)
  • Factory function to create field structure from parameter
options
FieldArrayOptions<Param, Node> | string
Configuration options or debug name string

Return Value

FieldArrayAtom
FieldArrayAtom<Param, Node>
Field array atom with the following properties:

Examples

Basic Array

import { reatomFieldArray } from '@reatom/framework'

const tagsArray = reatomFieldArray(['react', 'reatom'])

// Add items
const newTag = tagsArray.create('typescript')
console.log(tagsArray.array().length) // 3

// Access items
const tags = tagsArray.array()
console.log(tags[0].value()) // 'react'

// Remove items
tagsArray.remove(newTag)
console.log(tagsArray.array().length) // 2

// Clear all
tagsArray.clear()
console.log(tagsArray.array().length) // 0

Array with Factory Function

import { reatomFieldArray, reatomField } from '@reatom/framework'

const todosArray = reatomFieldArray(
  (text: string) => ({
    text: reatomField(text),
    completed: reatomField(false),
    createdAt: reatomField(new Date()),
  }),
  'todos'
)

// Create new todo
const todo = todosArray.create('Buy milk')

// Access todo fields
console.log(todo.text.value()) // 'Buy milk'
console.log(todo.completed.value()) // false

// Update todo
todo.completed.change(true)
todo.text.change('Buy organic milk')

Array with Initial State and Factory

import { reatomFieldArray } from '@reatom/framework'

const contactsArray = reatomFieldArray(
  ['alice@example.com', 'bob@example.com'],
  {
    create: (email: string) => ({
      email,
      verified: false,
    }),
    name: 'contacts',
  }
)

console.log(contactsArray.array().length) // 2
console.log(contactsArray.array()[0].email.value()) // 'alice@example.com'

// Add new contact
const newContact = contactsArray.create('charlie@example.com')
console.log(newContact.verified.value()) // false

Array Validation

import { reatomFieldArray } from '@reatom/framework'
import { z } from 'zod'

const itemsArray = reatomFieldArray([''], {
  name: 'items',
  validate: z.array(z.string()).min(2, 'At least 2 items required'),
  validateOnChange: true,
})

console.log(itemsArray.validation().error) // 'At least 2 items required'

itemsArray.create('Item 2')
console.log(itemsArray.validation().error) // undefined

Custom Validation Logic

import { reatomFieldArray } from '@reatom/framework'

const permissionsArray = reatomFieldArray(
  [{ resource: 'users', enabled: true }],
  {
    validate: ({ state }) => {
      const hasEnabled = state.some((item) => item.enabled())
      return hasEnabled ? undefined : 'At least one permission must be enabled'
    },
    validateOnChange: true,
  }
)

// Disable all permissions
permissionsArray.array()[0].enabled.set(false)
console.log(itemsArray.validation().error)
// 'At least one permission must be enabled'

Nested Arrays

import { reatomForm, reatomFieldArray } from '@reatom/framework'

const form = reatomForm({
  addresses: [
    {
      country: '',
      city: '',
      tags: ['home', 'primary'],
    },
  ],
})

const addresses = form.fields.addresses

// Add new address
const newAddress = addresses.create({
  country: 'USA',
  city: 'New York',
  tags: ['work'],
})

// Access nested array
const tags = newAddress.tags
tags.create('billing')
console.log(tags.array().length) // 2

Complex Nested Structure

import { reatomFieldArray, reatomBoolean } from '@reatom/framework'
import { withField } from '@reatom/framework'

const phoneNumbersArray = reatomFieldArray([], {
  create: ({ number, priority }: { number: string; priority: boolean }, name) => ({
    number,
    priority: reatomBoolean(priority, `${name}.priority`).extend(withField()),
  }),
  name: 'phoneNumbers',
})

const phone = phoneNumbersArray.create({
  number: '555-0123',
  priority: true,
})

console.log(phone.number.value()) // '555-0123'
console.log(phone.priority()) // true

// Toggle priority
phone.priority.toggle()
console.log(phone.priority()) // false

Moving Items

import { reatomFieldArray } from '@reatom/framework'

const playlistArray = reatomFieldArray([
  'Song 1',
  'Song 2',
  'Song 3',
])

// Move song from index 0 to index 2
playlistArray.move(0, 2)

const songs = playlistArray.array()
console.log(songs[0].value()) // 'Song 2'
console.log(songs[1].value()) // 'Song 3'
console.log(songs[2].value()) // 'Song 1'

Dynamic Reset

import { reatomFieldArray } from '@reatom/framework'

const itemsArray = reatomFieldArray(['a', 'b', 'c'])

itemsArray.create('d')
console.log(itemsArray.array().length) // 4

// Reset to initial state
itemsArray.reset()
console.log(itemsArray.array().length) // 3

// Reset to new state
itemsArray.reset(['x', 'y'])
console.log(itemsArray.array().length) // 2
console.log(itemsArray.array()[0].value()) // 'x'

Array with Cross-Field Validation

import { atom, reatomFieldArray } from '@reatom/framework'

const minLength = atom(2, 'minLength')

const itemsArray = reatomFieldArray(['a'], {
  validateOnChange: true,
  validate: ({ value }) => {
    if (value.length < minLength()) {
      return `Need at least ${minLength()} items`
    }
  },
})

console.log(itemsArray.validation().error) // 'Need at least 2 items'

itemsArray.create('b')
console.log(itemsArray.validation().error) // undefined

// Change requirement
minLength.set(3)
console.log(itemsArray.validation().error) // 'Need at least 3 items'

Dirty State Tracking

import { reatomFieldArray } from '@reatom/framework'

const itemsArray = reatomFieldArray(['a', 'b', 'c'])

console.log(itemsArray.focus().dirty) // false

const element = itemsArray.create('d')
console.log(itemsArray.focus().dirty) // true

// Remove the new element
itemsArray.remove(element)
console.log(itemsArray.focus().dirty) // false (back to initial state)

Async Array Validation

import { reatomFieldArray } from '@reatom/framework'
import { wrap } from '@reatom/framework'

const tagsArray = reatomFieldArray(['tag1'], {
  validate: async ({ value }) => {
    await wrap(sleep(100))
    if (value.length > 10) {
      throw new Error('Too many tags')
    }
  },
  validateOnChange: true,
})

for (let i = 0; i < 11; i++) {
  tagsArray.create(`tag${i}`)
}

console.log(tagsArray.validation().validating) // Promise

await wrap(tagsArray.validation().validating)
console.log(tagsArray.validation().error) // 'Too many tags'

Type Utilities

FieldArrayItem

Extract the type of a single element from a field array:
import { reatomFieldArray, type FieldArrayItem } from '@reatom/framework'

const usersArray = reatomFieldArray([{ name: 'Alice', age: 30 }])

type UserItem = FieldArrayItem<typeof usersArray>
// UserItem = { name: FieldAtom<string>, age: FieldAtom<number> }

isFieldArrayAtom

Type guard to check if an atom is a field array:
import { isFieldArrayAtom } from '@reatom/framework'

if (isFieldArrayAtom(someAtom)) {
  someAtom.create({ name: '', age: 0 })
}