Skip to main content

Programmatic Control

Attach a ref to <AutoForm> to get an AutoFormHandle — a superset of FormMethods — that lets you read and set values, trigger submission, reset the form, and check submission state from anywhere in your component tree.

import { useRef } from 'react'
import type { AutoFormHandle } from '@uniform-ts/core'

const formRef = useRef<AutoFormHandle<typeof schema>>(null)

<AutoForm ref={formRef} ... />

Available methods

AutoFormHandle exposes all FormMethods plus:

MemberTypeDescription
isSubmittingbooleantrue while the onSubmit handler is executing
setValue(field, value) => voidSet a specific field value
setValues(values: Partial<T>) => voidSet multiple field values at once
getValues() => TGet all current values
watch(field?) => T[field]Subscribe to a field value (or all values)
reset(values?) => voidReset the form, optionally to new default values
resetField(field) => voidReset a single field to its default value
setError(field, message) => voidSet a manual validation error
setErrors(errors) => voidSet errors on multiple fields at once
clearErrors(fields?) => voidClear one or all errors
submit() => voidProgrammatically trigger form submission
focus(field) => voidFocus a specific field by name

Live Example

An external control panel linked to the form via ref:

Live Editor
const schema = z.object({
  firstName: z.string().min(1, 'Required'),
  lastName: z.string().min(1, 'Required'),
  email: z.string().email(),
  role: z.enum(['viewer', 'editor', 'admin']),
})

const userForm = createForm(schema)

function App() {
  const formRef = React.useRef(null)
  const [info, setInfo] = React.useState('')

  const handleShowValues = () => {
    const values = formRef.current?.getValues()
    setInfo(JSON.stringify(values, null, 2))
  }

  const handleFill = () => {
    formRef.current?.reset({
      firstName: 'Jane',
      lastName: 'Doe',
      email: 'jane@example.com',
      role: 'editor',
    })
    setInfo('Form filled with demo data')
  }

  const handleClear = () => {
    formRef.current?.reset()
    setInfo('Form cleared')
  }

  return (
    <div style={{ fontFamily: 'system-ui', maxWidth: 520 }}>
      <div
        style={{
          display: 'flex',
          gap: 8,
          marginBottom: '1rem',
          flexWrap: 'wrap',
        }}
      >
        <button
          type='button'
          onClick={handleFill}
          style={{
            padding: '6px 14px',
            background: '#4F46E5',
            color: '#fff',
            border: 'none',
            borderRadius: 6,
            cursor: 'pointer',
          }}
        >
          Fill Demo Data
        </button>
        <button
          type='button'
          onClick={handleShowValues}
          style={{
            padding: '6px 14px',
            border: '1px solid var(--ifm-color-emphasis-300)',
            borderRadius: 6,
            cursor: 'pointer',
          }}
        >
          Show Values
        </button>
        <button
          type='button'
          onClick={handleClear}
          style={{
            padding: '6px 14px',
            border: '1px solid var(--ifm-color-emphasis-300)',
            borderRadius: 6,
            cursor: 'pointer',
            color: 'var(--ifm-color-danger)',
          }}
        >
          Clear
        </button>
      </div>
      <AutoForm
        ref={formRef}
        form={userForm}
        fields={{
          firstName: { label: 'First Name', span: 6 },
          lastName: { label: 'Last Name', span: 6 },
          email: { label: 'Email' },
          role: { label: 'Role' },
        }}
        onSubmit={(v) => setInfo(JSON.stringify(v, null, 2))}
      />
      {info && (
        <pre
          style={{
            marginTop: '1rem',
            background: 'var(--ifm-color-emphasis-200)',
            padding: '1rem',
            borderRadius: 6,
            fontSize: 12,
            overflow: 'visible',
            whiteSpace: 'pre-wrap',
          }}
        >
          {info}
        </pre>
      )}
    </div>
  )
}

render(<App />)
Result
Loading...