Skip to content

API reference

Formedible API reference

Lookup tables for hook config, form options, fields, pages, tabs, persistence, analytics, returned helpers, and native form props. Each section links to source, tests, or examples.

useFormedible hook

useFormedible takes UseFormedibleOptions<TFormValues>. It returns Form, the TanStack form instance, page helpers, and persistence helpers.

  • Signature in source: useFormedible<TFormValues extends FormedibleFormValues = FormedibleFormValues>(config: UseFormedibleOptions<TFormValues>).
  • The implementation passes formOptions.defaultValues to TanStack Form and builds validators from schema, crossFieldValidation, asyncValidation, and each field config.
  • Form closes over config, form state, analytics, pages, tabs, and persistence helpers.

UseFormedibleOptions

Every property is optional in the type: fields defaults to an empty list and defaultValues falls back to an empty object. Other options only matter when the hook or a child hook reads them.

  • Fields are normalized before rendering. Type aliases such as multiselect and colorPicker are compatibility inputs and normalize to the canonical renderer keys.
  • Tabs win over pages for field filtering. In use-formedible.tsx, activeFields checks tabs first, pages second, then falls back to all fields.
  • collapseLabel and expandLabel exist in the options type, but the hook source does not read them.
PropertyTypeDefaultDescription
fields
readonly FormedibleFieldConfig<TFormValues>[]
[]Field definitions rendered by FieldRenderer after normalization.
formOptions
FormedibleFormOptions<TFormValues>
undefinedDefault values plus submit/change/blur/focus/reset callbacks. defaultValues falls back to an empty object.
schema
unknown
undefinedStandard-schema input passed to buildFormValidators and buildFieldValidators.
crossFieldValidation
readonly FormedibleCrossFieldValidation<TFormValues>[]
undefinedRules with fields and validator(values), used by form and field validators.
asyncValidation
Partial<Record<Extract<keyof TFormValues, string> | string, FormedibleAsyncValidation<TFormValues>>>
undefinedField-name keyed async validators passed into buildFieldValidators.
pages
readonly FormediblePageConfig<TFormValues>[]
undefinedPage metadata consumed by useMultiPage and FormProgress.
tabs
readonly (string | FormedibleTabConfig<TFormValues>)[]
undefinedTab metadata consumed by useFormTabs and FormTabs.
progress
FormedibleProgressConfig
undefinedshowSteps and showPercentage passed to FormProgress.
validationSummary
boolean | FormedibleValidationSummaryConfig
truePost-submit summary of invalid fields. Object form sets autoNavigate and showBadges; both default true. false disables the summary and badges.
persistence
FormediblePersistenceConfig<TFormValues>
undefinedDraft save, load, restore, and clear config passed to useFormPersistence.
analytics
FormedibleAnalyticsConfig<TFormValues>
undefinedCallbacks passed to useFormAnalytics.
defaultComponents
Readonly<Record<string, FormedibleFieldComponent<TFormValues>>>
undefinedPer-type component overrides passed to FieldRenderer. Known type keys (including legacy aliases) are normalized; custom type strings match verbatim; unregistered types fall back to text.
globalWrapper
FormedibleFieldWrapper<TFormValues>
undefinedWrapper passed to FieldRenderer for rendered fields.
submitLabel
ReactNode
'Submit'Label for the single submit button or final page navigation submit.
nextLabel
ReactNode
'Next'Label for page navigation next button.
previousLabel
ReactNode
'Previous'Label for page navigation previous button.
onPageChange
(page: number, direction: 'next' | 'previous') => void
undefinedCalled after the analytics page-change call with destination page and direction.
autoSubmitOnChange
boolean
falseWhen truthy, field changes schedule form.handleSubmit.
autoSubmitDebounceMs
number
300Debounce for autoSubmitOnChange.
disabled
boolean
falseDisables the fieldset and forces rendered field configs to disabled.
loading
boolean
falseSets aria-busy and disables controls.
showSubmitButton
boolean
trueSet false to hide Formedible submit/navigation submit controls.
onFormReset
FormedibleFormEventHandler<TFormValues>
undefinedRuns after the native reset prop and formOptions.onReset.
onFormInput
FormedibleFormEventHandler<TFormValues>
undefinedRuns after the native onInput prop.
onFormInvalid
FormedibleFormEventHandler<TFormValues>
undefinedRuns after the native onInvalid prop.
onFormKeyDown
FormedibleFormEventHandler<TFormValues, KeyboardEvent>
undefinedRuns after the native onKeyDown prop.
onFormKeyUp
FormedibleFormEventHandler<TFormValues, KeyboardEvent>
undefinedRuns after the native onKeyUp prop.
onFormFocus
FormedibleFormEventHandler<TFormValues, FocusEvent>
undefinedRuns after the native onFocus prop.
onFormBlur
FormedibleFormEventHandler<TFormValues, FocusEvent>
undefinedRuns after the native onBlur prop.
collapseLabel
ReactNode
undefinedTyped compatibility label. The current hook source does not read it.
expandLabel
ReactNode
undefinedTyped compatibility label. The current hook source does not read it.
formClassName
string
undefinedClass name passed to FormLayout, not the native form element.

Field config lookup

Fields drive rendering, validation, pages, tabs, nested objects, and arrays.

  • Supported type strings are declared in FormedibleFieldType. NormalizedFieldType excludes the legacy aliases after normalization.
  • Nested fields are carried through nestedFields, arrayConfig.objectConfig.fields, and objectConfig.fields.
  • emailConfig is typed as never. Use schema or validation for email-specific rules.
PropertyTypeDefaultDescription
name
Extract<keyof TFormValues, string> | string
RequiredField path sent to TanStack Form.
type
FormedibleFieldType
'text' after normalizationRenderer key or compatibility alias.
label / description / placeholder
ReactNode / ReactNode / string
undefinedRendered field copy. Dynamic text is resolved at render time.
disabled / required
boolean
falseControl state and built-in required validation input.
page / tab / section
number / string / string | FormedibleFieldSection
undefinedLayout metadata consumed by pages, tabs, and section headers.
conditional
string | ((values: TFormValues) => boolean)
undefinedVisibility condition checked against current values.
options
readonly FormedibleFieldOption[] | ((values: TFormValues) => readonly FormedibleFieldOption[])
undefinedStatic or values-derived options for option fields.
nestedFields / arrayConfig / objectConfig
Nested field config objects
undefinedNested object and array field configuration.
validation / inlineValidation
FormedibleFieldValidation<TFormValues> / FormedibleInlineValidation<TFormValues>
undefinedField-level sync, schema-like, and inline async validation config.
component / wrapper
FormedibleFieldComponent<TFormValues> / FormedibleFieldWrapper<TFormValues>
undefinedField-level rendering override and wrapper.

FormedibleFormOptions

formOptions is required. Put defaultValues here, plus submit and field event callbacks.

  • defaultValues is the only required property inside FormedibleFormOptions.
  • onSubmit receives { value, formApi } after onFormComplete analytics and before persistence is cleared.
  • onChange receives a shallow next value object for the changed field name, then auto-submit may be scheduled.
  • onSubmitInvalid is forwarded verbatim to the TanStack Form useForm config and runs when a submit fails validation.
PropertyTypeDefaultDescription
defaultValues
TFormValues
RequiredInitial values passed to useForm.
onSubmit
(context: FormedibleFormEventContext<TFormValues>) => void | Promise<void>
undefinedSubmit callback with value and optional formApi context.
onChange
(context: FormedibleFormEventContext<TFormValues>) => void
undefinedRuns from field onChange after TanStack field change and analytics field change tracking.
onBlur
(context: FormedibleFormEventContext<TFormValues>) => void
undefinedRuns from field onBlur after field.handleBlur and analytics blur/error/complete tracking.
onFocus
(context: FormedibleFormEventContext<TFormValues>) => void
undefinedRuns from field onFocus after analytics focus tracking.
onReset
(context: FormedibleFormEventContext<TFormValues>) => void
undefinedRuns from the rendered form reset handler after native onReset.
onSubmitInvalid
(props: { value: TFormValues; formApi: AnyFormApi; meta: unknown }) => void
undefinedForwarded verbatim to the underlying useForm config. Runs when a submit fails validation.

Persistence config

Persistence stores values, timestamp, and currentPage when present. The hook also returns save, load, and clear helpers.

  • Payload shape is { values: Partial<TFormValues>, timestamp: number, currentPage?: number }.
  • Storage defaults to sessionStorage. localStorage is selected only when storage is localStorage; SSR returns no storage.
  • restoreOnMount calls loadFromStorage in an effect. Value changes schedule saveToStorage after debounceMs ?? 500.
PropertyTypeDefaultDescription
key
string
RequiredStorage key used for the persisted JSON payload.
storage
'localStorage' | 'sessionStorage'
'sessionStorage'Storage target selected by getConfiguredStorage.
debounceMs
number
500Delay before value-change saves.
exclude
readonly (Extract<keyof TFormValues, string> | string)[]
[]Top-level keys excluded from persisted values.
restoreOnMount
boolean
falseLoads storage on mount and restores currentPage when stored currentPage is <= totalPages.

Analytics config

Analytics callbacks use positional arguments. The runtime emits form, field, page, abandon, and reset events.

  • onFormStart runs once from the analytics hook mount effect.
  • Field events are emitted by FieldRenderer controller handlers in use-formedible.tsx.
  • Page changes pass fromPage, toPage, timeSpent, and validation state for the page being left.
  • Tab switch, first tab visit, and submission performance callbacks are restored and emitted by the current runtime.
  • Page completion, tab completion, and render/validation performance callbacks are superseded: typed as never and never emitted.
PropertyTypeDefaultDescription
onFormStart
(timestamp: number) => void
undefinedRuns on mount.
onFieldFocus
(fieldName: string, timestamp: number) => void
undefinedRuns when a Formedible field receives focus.
onFieldBlur
(fieldName: string, timeSpent: number) => void
undefinedtimeSpent is focus-derived; missing focus yields 0.
onFieldChange
(fieldName: string, value: unknown, timestamp: number) => void
undefinedRuns from field onChange.
onFieldComplete
(fieldName: string, isValid: boolean, timeSpent: number) => void
undefinedRuns on blur after errors are collected.
onFieldError
(fieldName: string, errors: readonly string[], timestamp: number) => void
undefinedRuns on blur when formatted errors exist.
onPageChange
(fromPage: number, toPage: number, timeSpent: number, pageValidationState?: { readonly hasErrors: boolean; readonly completionPercentage: number }) => void
undefinedRuns through useMultiPage changePage.
onTabChange
(fromTab: string, toTab: string, timeSpent: number, tabCompletionState?: { readonly completionPercentage: number; readonly hasErrors: boolean }) => void
undefinedRuns through useFormTabs changeTab with the legacy tab arguments.
onTabFirstVisit
(tabId: string, timestamp: number) => void
undefinedFires the first time a tab becomes active, including the initial tab on mount.
onFormComplete
(timeSpent: number, formData: TFormValues) => void
undefinedRuns before formOptions.onSubmit.
onFormAbandon
(completionPercentage: number, context?: AbandonContext) => void
undefinedRuns on unmount unless completion was tracked.
onFormReset
(timestamp: number, reason?: string) => void
undefinedRendered form reset path passes reason reset.
onSubmissionPerformance
(submissionTime: number, validationTime: number, processingTime: number) => void
undefinedRuns after a successful submit with total time, validation time (always 0), and processing time.

Pages, tabs, and progress

Pages and tabs are separate modes. When visible tabs exist, active tab filtering wins over page filtering.

  • Visible pages come from field page numbers, sorted ascending, filtered by page conditional and field conditional. Empty results fall back to [1].
  • progressValue is 100 for a single visible page. Multi-page flows use currentIndex / (totalPages - 1) * 100.
  • Tabs normalize string tabs to { id, label }. If tabs are omitted, field tab ids create the tab list.
PropertyTypeDefaultDescription
FormediblePageConfig.page
number
RequiredPage number matched by field.page.
FormediblePageConfig.title
ReactNode
RequiredRendered in FormProgress for the current page.
FormediblePageConfig.description
ReactNode
undefinedRendered below the current page title.
FormediblePageConfig.conditional
string | ((values: TFormValues) => boolean)
undefinedHides the page when false.
FormedibleTabConfig.id
string
RequiredMatched by field.tab.
FormedibleTabConfig.label
ReactNode
RequiredRendered as the tab label.
FormedibleTabConfig.description
ReactNode
undefinedRendered by FormTabs when present.
FormedibleTabConfig.conditional
string | ((values: TFormValues) => boolean)
undefinedHides the tab when false.
FormedibleProgressConfig.showSteps
boolean
falseShows step count text in FormProgress.
FormedibleProgressConfig.showPercentage
boolean
falseShows progress percentage text in FormProgress.

Return value

The hook returns Form, the TanStack form instance, page state, page helpers, and persistence helpers. Removed validation debug helpers are not returned.

  • Page helpers are returned even for single-page forms. useMultiPage falls back to visiblePages [1] and progressValue 100.
  • Persistence helpers are returned even when persistence is not configured; they no-op or return undefined when storage is unavailable.
  • Tests keep the return contract explicit and list removed fields: crossFieldErrors, asyncValidationStates, validateCrossFields, validateFieldAsync.
PropertyTypeDefaultDescription
Form
(props: ComponentProps<'form'>) => ReactNode
ReturnedComponent that renders FormRoot, fields, tabs/pages, navigation, and submit handling.
form
Return value from useForm<TFormValues>
ReturnedUnderlying TanStack Form instance.
currentPage
number
1Current page number from useMultiPage.
totalPages
number
1Count of visible pages, with fallback to 1.
visiblePages
readonly number[]
[1]Sorted visible page numbers.
goToNextPage
() => void
ReturnedMoves to the next visible page when one exists.
goToPreviousPage
() => void
ReturnedMoves to the previous visible page when one exists.
setCurrentPage
Dispatch<SetStateAction<number>>
ReturnedSets current page state directly; invalid pages are corrected by useMultiPage effect.
isFirstPage
boolean
trueTrue when currentPage is at visiblePages index 0.
isLastPage
boolean
trueTrue when currentPage is at the final visiblePages index.
progressValue
number
100Progress percentage from useMultiPage.
saveToStorage
() => void
ReturnedWrites current values, timestamp, and currentPage through configured persistence.
loadFromStorage
() => PersistedFormPayload<TFormValues> | undefined
ReturnedLoads storage, sets form values, restores currentPage when stored currentPage is <= totalPages, and returns the parsed payload.
clearStorage
() => void
ReturnedRemoves the configured persistence payload.

Form component props

The returned Form accepts native form props. Submit is handled by form.handleSubmit, not the native onSubmit prop.

  • Native event props run first. Matching onForm* callbacks from UseFormedibleOptions run next with event plus formApi context.
  • The returned Form prevents default submit, stops propagation, and calls form.handleSubmit. Put submit work in formOptions.onSubmit.
  • className goes to the native form element. formClassName goes to the internal FormLayout.
PropertyTypeDefaultDescription
className
string
undefinedClass name for the native form element.
aria-label
string
undefinedAccessible name when the form lacks a linked visible heading.
aria-labelledby
string
undefinedReferences visible text that names the form.
id
string
undefinedNative form id.
name
string
undefinedNative form name.
autoComplete
string
undefinedNative autocomplete mode for the form.
noValidate
boolean
falseNative flag that disables browser validation UI.
data-*
string | number | boolean | undefined
undefinedCustom data attributes forwarded to the native form.
onBlur / onFocus
FocusEventHandler<HTMLFormElement>
undefinedNative focus callbacks, followed by onFormBlur and onFormFocus from hook config.
onInput / onInvalid
FormEventHandler<HTMLFormElement>
undefinedNative input and invalid callbacks, followed by onFormInput and onFormInvalid.
onKeyDown / onKeyUp
KeyboardEventHandler<HTMLFormElement>
undefinedNative keyboard callbacks, followed by onFormKeyDown and onFormKeyUp.
onReset
FormEventHandler<HTMLFormElement>
undefinedNative reset callback, followed by formOptions.onReset, onFormReset, and analytics reset.
onSubmit
FormEventHandler<HTMLFormElement>
Handled by FormedibleThe returned Form ignores the native onSubmit prop and calls form.handleSubmit.

Install the registry item

Add the built registry item to your app, then import the copied hook from your local UI package.

pnpm dlx shadcn@latest add https://formedible.dev/r/formedible-core.json

Typed fields over TanStack Form

Formedible renders shadcn-compatible fields while keeping TanStack Form in reach.

import { z } from 'zod';

import { useFormedible } from '@/components/ui/formedible/hooks/use-formedible';

const onboardingSchema = z.object({
  name: z.string().min(2),
  plan: z.enum(['starter', 'team', 'enterprise']),
  needsMigration: z.boolean(),
});

type OnboardingValues = z.infer<typeof onboardingSchema>;

type WorkspaceRecord = OnboardingValues & {
  slug: string;
};

const workspaceRecords: WorkspaceRecord[] = [];

function createWorkspace(values: OnboardingValues): WorkspaceRecord {
  const workspace = {
    ...values,
    slug: values.name.trim().toLowerCase().replace(/\s+/g, '-'),
  };

  workspaceRecords.push(workspace);
  return workspace;
}

export function OnboardingForm() {
  const { Form } = useFormedible<OnboardingValues>({
    fields: [
      { name: 'name', type: 'text', label: 'Workspace name', required: true },
      { name: 'plan', type: 'radio', label: 'Plan', options: ['starter', 'team', 'enterprise'] },
      { name: 'needsMigration', type: 'switch', label: 'Import an existing form system?' },
    ],
    schema: onboardingSchema,
    formOptions: {
      defaultValues: { name: '', plan: 'team', needsMigration: false },
      onSubmit: ({ value }) => {
        createWorkspace(value);
      },
    },
  });

  return <Form />;
}

Formedible

TanStack Form, shadcn/ui, Zod