Field groups and your own shell

Render a config into a form you build yourself, and reuse one config at several paths.

createForm renders the <form> element, its submission and the fields. When a form needs more around its fields, you build that shell yourself with the registry's formHook and keep the config for the fields: fields and layout take the same types in both places. Ways in compares the two.

Your own shell

formHook is TanStack's createFormHook result for the registry's widgets and form components: useAppForm, withForm, withFieldGroup and useTypedAppFormContext. createForm builds its forms with the very same hook. Fields renders a config into any form from it, as often as you like:

export const  = () => {
  const  = .({
    : { : '', : '' },
    : ({  }) => (),
  })

  return (
    <.>
      <form
        onSubmit={() => {
          .()
          void .()
        }}
      >
        <Fields ={} fields={{ name: { : 'text', : { : 'Name' } } }} />
        <hr />
        <Fields ={} fields={{ note: { : 'text', : { : 'Note' } } }} />
        <. ={() => .}>
          {() => (
            <button type="submit" aria-disabled={}>
              Save
            </button>
          )}
        </.>
      </form>
    </.>
  )
}

This is TanStack's own API around Fields: every option of useAppForm is yours, including hooks in the component, and the form object has its whole API, validators included. The registered body slot does not wrap it; the shell is yours, and so is the wrapper.

Field groups

TanStack's withFieldGroup builds a component for a part of a form that can sit at more than one path. Fields renders into a group as it renders into a form, so a group can take its fields from a config. FieldsOf types such a config for the group's own value:

type  = { : string; : string }

const : <typeof , > = {
  : { : 'text', : { : 'Street' } },
  : { : 'text', : { : 'City' } },
}

const :  = { : '', : '' }

const  = .({
  : ,
  : ({  }) => <Fields ={} fields={} />,
})

export const  = () => {
  const  = .({
    : { : , :  },
  })

  return (
    <.>
      < form={} ="billing" />
      < form={} ="shipping" />
    </.>
  )
}

TanStack maps the paths, so the same config renders at billing.street and at shipping.street. Inside a group, conditions and derived props see the group's values, not the whole form's.

LayoutOf types a layout for such a config the same way, and both types also share a part of a config between forms built with createForm.