Skip to content

Quickstart

Terminal window
pnpm add form-meta
# or: npm i form-meta / yarn add form-meta

RawFieldsMeta is tied to your form data type. Use nested + subfields for objects/arrays and attach type, schema, and custom extends.

import { z } from 'zod'
import { FieldsMeta } from 'form-meta'
import type { RawFieldsMeta } from 'form-meta'
type FieldType = 'input' | 'select'
interface FieldExtends {
label?: string
placeholder?: string
}
interface ArticleForm {
title: string
sections: Array<{ heading: string; body: string }>
}
type ArticleFields = RawFieldsMeta<ArticleForm, FieldType, FieldExtends>
const rawFieldsMeta: ArticleFields = {
title: {
type: 'input',
schema: z.string().min(1, { message: 'Required' }),
extends: { label: 'Title' },
},
sections: {
nested: 'array',
subfields: {
heading: { type: 'input', extends: { label: 'Heading' } },
body: { type: 'input', extends: { label: 'Body' } },
},
},
}

flatten lifts child fields of objects/arrays into dotted keys; purge removes those wrapper nodes. Resolved fields include name, path, fullName, and parent.

const fm = new FieldsMeta<FieldType, FieldExtends>()
const resolved = fm.resolve(rawFieldsMeta, {
/**
* Usually you may just what to flatten 'object' nested types.
*
* And if you have a fixed number of array items, maybe define a 'object' with
* indexed keys instead is better.
*/
flatten: ['object', 'array'],
purge: ['object'],
parentCircleReferences: false,
})
// resolved keys: ['title', 'sections', 'sections.heading', 'sections.body']
// resolved['sections.heading'].fullName(2) -> "sections[2].heading"
  • Use field.fullName(...indices) as the form field name (pass indices for array items).
  • field.schema plugs into validation (Standard Schema v1).
  • field.extends carries UI config like label/placeholder/options.

In the playground, TanStack Form + Vue renders array fields dynamically. See src/components/Vue/Form.vue, FormField.vue, FormFieldArray.vue.

  • flatten: ('object' | 'array')[]: flatten specified nested types using dotted keys.
  • purge: ('object' | 'array')[]: drop specified nested nodes (children can remain).
  • parentCircleReferences: boolean: keep parent circular refs (default true).
  • fullNameFormatter: customize path generation.