Skip to content

useRepeater ​

Manages a list of repeatable form blocks. Each block has its own scoped schema and model. Handles add, delete, duplicate, drag reorder, uniqueness constraints, and sync between the internal repeater representation and the flat modelValue array.

File: vue/src/js/hooks/useRepeater.js
Props factory: makeRepeaterProps


Usage ​

js
import { useRepeater, makeRepeaterProps } from '@/hooks'

const props = defineProps({ ...makeRepeaterProps() })
const {
  repeaterModels,
  repeaterSchemas,
  totalRepeats,
  isAddible,
  isDeletable,
  addRepeaterBlock,
  deleteRepeaterBlock,
  duplicateRepeaterBlock,
  onUpdateRepeaterModel
} = useRepeater(props, context)

Props (via makeRepeaterProps) ​

Extends makeInputProps and makeDraggableProps plus:

PropTypeDefaultDescription
modelValueArray[]The current list of block data
schemaObject{}Input schema shared by all blocks
maxNumber-1Maximum blocks (-1 = unlimited)
minNumber-1Minimum blocks (-1 = none required)
labelString''Repeater label
singularLabelString—Label for a single block (used in add button)
addButtonTextStringt('ADD NEW')Add button label
hasButtonLabelBooleanfalseAppend singularLabel to the add button
noAddButtonBooleanfalseHide the add button
noHeadersBooleanfalseSkip removing labels from block schema
isUniqueBooleanfalseEnforce unique values in the first field
uniqueValueString'id'Key used to determine uniqueness
uniqueFieldStringnullSchema field name that must be unique
asObjectBooleanfalseStore as { [uniqueField]: {rest} } instead of an array
formColObject{ cols: 12 }Grid column for each block
autoIdGeneratorBooleantrueAssign an id equal to the block index
idResetterStringnullField key — resets id when this field changes
noWaitSourceLoadingBooleanfalseDon't wait for source loading before rendering

Returns ​

NameTypeDescription
repeaterModelsRef<Array>Internal hydrated model list (namespaced keys)
repeaterSchemasComputedRef<Array>Per-block schema list with namespaced field names
totalRepeatsComputedRef<Number>Number of blocks
hasRepeaterModelsComputedRef<Boolean>True when at least one block exists
isAddibleComputedRef<Boolean>True when a new block can be added
isDeletableComputedRef<Boolean>True when a block can be deleted
addButtonIsActiveComputedRef<Boolean>True when the add button should be enabled
headersArrayColumn header labels derived from the schema
selectFieldSlotsComputedRef<Array>Slot definitions for schema fields that declare slots
hasSchemaInputSourceLoadingComputedRef<Boolean>True while any schema input is loading remote data
addRepeaterBlock() => voidAppend a new blank block
deleteRepeaterBlock(index) => voidRemove block at index
duplicateRepeaterBlock(index) => voidClone block at index
onUpdateRepeaterModel(value, index) => voidUpdate a block's model when a field changes
onUpdateRepeaterSchema(value, index) => voidUpdate raw schema when a field updates it (e.g. cascade)

Field namespacing ​

To isolate blocks from each other, every field name is namespaced:

repeater{id}[{blockIndex}][{fieldName}]
// e.g. repeater42[0][title]

The hook transparently hydrates (namespace) and parses (strip namespace) models on write and read.

Uniqueness mode ​

When isUnique: true, each block's designated uniqueField must have a distinct value. The hook:

  • Tracks uniqueFilledValues across all blocks.
  • Filters the available items in the unique field's select down to unused values.
  • Disables the add button when all available values are taken.
  • Supports asObject: true to store as { [uniqueField]: { ...rest } } instead of an array.

See Also ​