S

useFormState

Main orchestrator hook — builds Zod schemas, sets up React Hook Form, coordinates plugins, steps, and submission.

useFormState

The central hook that wires everything together. It builds the Zod schema from your field configs, initializes React Hook Form, runs plugin lifecycle hooks, and coordinates step navigation with submission.

You probably don’t need this directly. The <Form /> component calls useFormState internally. Use it only when building a fully custom form UI from scratch.


Signature

import { useFormState } from '@saastro/forms';

const {
  currentStepId,
  loading,
  submitted,
  error,
  methods,
  nextStep,
  prevStep,
  handleSubmit,
  validateAndNext,
  isLast,
  getSuccessMessage,
  getErrorMessage,
  resetError,
  stepHistory,
} = useFormState({ config, fields, steps });

Parameters

ParameterTypeRequiredDescription
configFormConfigYesThe form configuration (from FormBuilder.build())
fieldsFieldsYesField definitions map
stepsStepsNoStep definitions. If omitted, a single default step with all fields is created

Return Value

PropertyTypeDescription
currentStepIdstringActive step ID
loadingbooleantrue while submitting
submittedbooleantrue after successful submission
errorError | nullLast submission error
methodsUseFormReturnReact Hook Form instance (register, control, watch, etc.)
nextStep(values) => booleanNavigate to the next step
prevStep() => booleanNavigate to the previous step
handleSubmit(data) => Promise<void>Submit handler (runs plugin hooks + actions)
validateAndNext(e?) => Promise<void>Validate current step, then navigate or submit
isLastbooleanWhether the current step is the last one
getSuccessMessage() => stringResolve the success message
getErrorMessage() => stringResolve the error message
resetError() => voidClear the error state
stepHistorystring[]Stack of visited step IDs

What It Does Internally

  1. Plugin config transform — If a pluginManager exists, runs transformConfig() before anything else
  2. Schema compilation — For each field, converts ValidationRules or raw Zod schemas into a unified z.object(). Chains any customValidators as superRefine calls
  3. Default values — Assigns per-type defaults: false for checkbox/switch, [] for groups, '' for text fields, overridden by field.value
  4. React Hook Form — Creates a useForm instance with zodResolver(schema)
  5. Plugin lifecycle — Fires onFormInit on mount, subscribes onFieldChange via watch()
  6. Step orchestration — Delegates to useFormSteps, useFormValidation, useFormSubmit
  7. validateAndNext — Validates current step fields, then calls nextStep() with conditional routing. Fires onStepChange hooks. On the last step, submits instead

Example: Custom Form UI

import { useFormState } from '@saastro/forms';
import { FormProvider } from 'react-hook-form';

function CustomForm({ config }) {
  const {
    methods,
    currentStepId,
    loading,
    submitted,
    error,
    validateAndNext,
    prevStep,
    isLast,
    stepHistory,
    getSuccessMessage,
  } = useFormState({
    config,
    fields: config.fields,
    steps: config.steps,
  });

  if (submitted) return <p>{getSuccessMessage()}</p>;

  return (
    <FormProvider {...methods}>
      <form onSubmit={methods.handleSubmit(() => validateAndNext())}>
        <p>Step: {currentStepId}</p>

        {/* Render fields for current step */}
        {config.steps[currentStepId].fields.map((name) => (
          <input key={name} {...methods.register(name)} placeholder={name} />
        ))}

        {stepHistory.length > 0 && (
          <button type="button" onClick={prevStep}>
            Back
          </button>
        )}
        <button type="submit" disabled={loading}>
          {isLast ? 'Submit' : 'Next'}
        </button>

        {error && <p>{error.message}</p>}
      </form>
    </FormProvider>
  );
}