Headless Mode

Use FormForge's hooks and engine without any built-in UI. Full control over rendering — bring your own components.

useFormForge

import { useFormForge } from '@formforges/react'
import { FormProvider, FormRenderer } from '@formforges/react'

function MyForm() {
  const { form, values, errors, isSubmitting, submit, reset } = useFormForge({
    schema,
    defaultValues: { email: '' },
    onSubmit: async (values) => {
      await fetch('/api/submit', { method: 'POST', body: JSON.stringify(values) })
    },
  })

  return (
    <FormProvider form={form}>
      <FormRenderer schema={schema} />

      <div className="flex gap-3 mt-4">
        <button onClick={submit} disabled={isSubmitting}>
          {isSubmitting ? 'Saving...' : 'Save'}
        </button>
        <button onClick={reset} type="button">
          Reset
        </button>
      </div>

      <pre>{JSON.stringify(values, null, 2)}</pre>
    </FormProvider>
  )
}

useField — custom field component

import { useField } from '@formforges/react'

function StarRating({ fieldKey }: { fieldKey: string }) {
  const { value, error, touched, onChange, onBlur } = useField(fieldKey)

  return (
    <div>
      {[1, 2, 3, 4, 5].map((star) => (
        <button
          key={star}
          type="button"
          onClick={() => onChange(star)}
          onBlur={onBlur}
          style={{ color: Number(value) >= star ? 'gold' : 'gray' }}
        >
          ★
        </button>
      ))}
      {touched && error && <p>{error}</p>}
    </div>
  )
}

createForm — no React

import { createForm } from '@formforges/core'

const form = createForm({
  schema,
  onSubmit: async (values) => console.log(values),
})

// Subscribe to a single field
const unsub = form.store.subscribeField('email', () => {
  console.log('email changed:', form.store.getFieldState('email')?.value)
})

form.setValue('email', 'test@example.com')
await form.validate()
await form.submit()
form.reset()
unsub()

useFormForge return values

PropertyTypeDescription
formFormInstanceRaw form instance
valuesTValuesCurrent field values (reactive)
errorsRecord<string, string | null>Current errors
isSubmittingbooleanSubmit in progress
isSubmittedbooleanSubmitted successfully
submit() => Promise<void>Trigger validation + submit
reset() => voidReset to default values