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.
| Property | Type | Default | Description |
|---|---|---|---|
fields | readonly FormedibleFieldConfig<TFormValues>[] | [] | Field definitions rendered by FieldRenderer after normalization. |
formOptions | FormedibleFormOptions<TFormValues> | undefined | Default values plus submit/change/blur/focus/reset callbacks. defaultValues falls back to an empty object. |
schema | unknown | undefined | Standard-schema input passed to buildFormValidators and buildFieldValidators. |
crossFieldValidation | readonly FormedibleCrossFieldValidation<TFormValues>[] | undefined | Rules with fields and validator(values), used by form and field validators. |
asyncValidation | Partial<Record<Extract<keyof TFormValues, string> | string, FormedibleAsyncValidation<TFormValues>>> | undefined | Field-name keyed async validators passed into buildFieldValidators. |
pages | readonly FormediblePageConfig<TFormValues>[] | undefined | Page metadata consumed by useMultiPage and FormProgress. |
tabs | readonly (string | FormedibleTabConfig<TFormValues>)[] | undefined | Tab metadata consumed by useFormTabs and FormTabs. |
progress | FormedibleProgressConfig | undefined | showSteps and showPercentage passed to FormProgress. |
validationSummary | boolean | FormedibleValidationSummaryConfig | true | Post-submit summary of invalid fields. Object form sets autoNavigate and showBadges; both default true. false disables the summary and badges. |
persistence | FormediblePersistenceConfig<TFormValues> | undefined | Draft save, load, restore, and clear config passed to useFormPersistence. |
analytics | FormedibleAnalyticsConfig<TFormValues> | undefined | Callbacks passed to useFormAnalytics. |
defaultComponents | Readonly<Record<string, FormedibleFieldComponent<TFormValues>>> | undefined | Per-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> | undefined | Wrapper 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 | undefined | Called after the analytics page-change call with destination page and direction. |
autoSubmitOnChange | boolean | false | When truthy, field changes schedule form.handleSubmit. |
autoSubmitDebounceMs | number | 300 | Debounce for autoSubmitOnChange. |
disabled | boolean | false | Disables the fieldset and forces rendered field configs to disabled. |
loading | boolean | false | Sets aria-busy and disables controls. |
showSubmitButton | boolean | true | Set false to hide Formedible submit/navigation submit controls. |
onFormReset | FormedibleFormEventHandler<TFormValues> | undefined | Runs after the native reset prop and formOptions.onReset. |
onFormInput | FormedibleFormEventHandler<TFormValues> | undefined | Runs after the native onInput prop. |
onFormInvalid | FormedibleFormEventHandler<TFormValues> | undefined | Runs after the native onInvalid prop. |
onFormKeyDown | FormedibleFormEventHandler<TFormValues, KeyboardEvent> | undefined | Runs after the native onKeyDown prop. |
onFormKeyUp | FormedibleFormEventHandler<TFormValues, KeyboardEvent> | undefined | Runs after the native onKeyUp prop. |
onFormFocus | FormedibleFormEventHandler<TFormValues, FocusEvent> | undefined | Runs after the native onFocus prop. |
onFormBlur | FormedibleFormEventHandler<TFormValues, FocusEvent> | undefined | Runs after the native onBlur prop. |
collapseLabel | ReactNode | undefined | Typed compatibility label. The current hook source does not read it. |
expandLabel | ReactNode | undefined | Typed compatibility label. The current hook source does not read it. |
formClassName | string | undefined | Class 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.
| Property | Type | Default | Description |
|---|---|---|---|
name | Extract<keyof TFormValues, string> | string | Required | Field path sent to TanStack Form. |
type | FormedibleFieldType | 'text' after normalization | Renderer key or compatibility alias. |
label / description / placeholder | ReactNode / ReactNode / string | undefined | Rendered field copy. Dynamic text is resolved at render time. |
disabled / required | boolean | false | Control state and built-in required validation input. |
page / tab / section | number / string / string | FormedibleFieldSection | undefined | Layout metadata consumed by pages, tabs, and section headers. |
conditional | string | ((values: TFormValues) => boolean) | undefined | Visibility condition checked against current values. |
options | readonly FormedibleFieldOption[] | ((values: TFormValues) => readonly FormedibleFieldOption[]) | undefined | Static or values-derived options for option fields. |
nestedFields / arrayConfig / objectConfig | Nested field config objects | undefined | Nested object and array field configuration. |
validation / inlineValidation | FormedibleFieldValidation<TFormValues> / FormedibleInlineValidation<TFormValues> | undefined | Field-level sync, schema-like, and inline async validation config. |
component / wrapper | FormedibleFieldComponent<TFormValues> / FormedibleFieldWrapper<TFormValues> | undefined | Field-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.
| Property | Type | Default | Description |
|---|---|---|---|
defaultValues | TFormValues | Required | Initial values passed to useForm. |
onSubmit | (context: FormedibleFormEventContext<TFormValues>) => void | Promise<void> | undefined | Submit callback with value and optional formApi context. |
onChange | (context: FormedibleFormEventContext<TFormValues>) => void | undefined | Runs from field onChange after TanStack field change and analytics field change tracking. |
onBlur | (context: FormedibleFormEventContext<TFormValues>) => void | undefined | Runs from field onBlur after field.handleBlur and analytics blur/error/complete tracking. |
onFocus | (context: FormedibleFormEventContext<TFormValues>) => void | undefined | Runs from field onFocus after analytics focus tracking. |
onReset | (context: FormedibleFormEventContext<TFormValues>) => void | undefined | Runs from the rendered form reset handler after native onReset. |
onSubmitInvalid | (props: { value: TFormValues; formApi: AnyFormApi; meta: unknown }) => void | undefined | Forwarded 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.
| Property | Type | Default | Description |
|---|---|---|---|
key | string | Required | Storage key used for the persisted JSON payload. |
storage | 'localStorage' | 'sessionStorage' | 'sessionStorage' | Storage target selected by getConfiguredStorage. |
debounceMs | number | 500 | Delay before value-change saves. |
exclude | readonly (Extract<keyof TFormValues, string> | string)[] | [] | Top-level keys excluded from persisted values. |
restoreOnMount | boolean | false | Loads 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.
| Property | Type | Default | Description |
|---|---|---|---|
onFormStart | (timestamp: number) => void | undefined | Runs on mount. |
onFieldFocus | (fieldName: string, timestamp: number) => void | undefined | Runs when a Formedible field receives focus. |
onFieldBlur | (fieldName: string, timeSpent: number) => void | undefined | timeSpent is focus-derived; missing focus yields 0. |
onFieldChange | (fieldName: string, value: unknown, timestamp: number) => void | undefined | Runs from field onChange. |
onFieldComplete | (fieldName: string, isValid: boolean, timeSpent: number) => void | undefined | Runs on blur after errors are collected. |
onFieldError | (fieldName: string, errors: readonly string[], timestamp: number) => void | undefined | Runs on blur when formatted errors exist. |
onPageChange | (fromPage: number, toPage: number, timeSpent: number, pageValidationState?: { readonly hasErrors: boolean; readonly completionPercentage: number }) => void | undefined | Runs through useMultiPage changePage. |
onTabChange | (fromTab: string, toTab: string, timeSpent: number, tabCompletionState?: { readonly completionPercentage: number; readonly hasErrors: boolean }) => void | undefined | Runs through useFormTabs changeTab with the legacy tab arguments. |
onTabFirstVisit | (tabId: string, timestamp: number) => void | undefined | Fires the first time a tab becomes active, including the initial tab on mount. |
onFormComplete | (timeSpent: number, formData: TFormValues) => void | undefined | Runs before formOptions.onSubmit. |
onFormAbandon | (completionPercentage: number, context?: AbandonContext) => void | undefined | Runs on unmount unless completion was tracked. |
onFormReset | (timestamp: number, reason?: string) => void | undefined | Rendered form reset path passes reason reset. |
onSubmissionPerformance | (submissionTime: number, validationTime: number, processingTime: number) => void | undefined | Runs 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.
| Property | Type | Default | Description |
|---|---|---|---|
FormediblePageConfig.page | number | Required | Page number matched by field.page. |
FormediblePageConfig.title | ReactNode | Required | Rendered in FormProgress for the current page. |
FormediblePageConfig.description | ReactNode | undefined | Rendered below the current page title. |
FormediblePageConfig.conditional | string | ((values: TFormValues) => boolean) | undefined | Hides the page when false. |
FormedibleTabConfig.id | string | Required | Matched by field.tab. |
FormedibleTabConfig.label | ReactNode | Required | Rendered as the tab label. |
FormedibleTabConfig.description | ReactNode | undefined | Rendered by FormTabs when present. |
FormedibleTabConfig.conditional | string | ((values: TFormValues) => boolean) | undefined | Hides the tab when false. |
FormedibleProgressConfig.showSteps | boolean | false | Shows step count text in FormProgress. |
FormedibleProgressConfig.showPercentage | boolean | false | Shows 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.
| Property | Type | Default | Description |
|---|---|---|---|
Form | (props: ComponentProps<'form'>) => ReactNode | Returned | Component that renders FormRoot, fields, tabs/pages, navigation, and submit handling. |
form | Return value from useForm<TFormValues> | Returned | Underlying TanStack Form instance. |
currentPage | number | 1 | Current page number from useMultiPage. |
totalPages | number | 1 | Count of visible pages, with fallback to 1. |
visiblePages | readonly number[] | [1] | Sorted visible page numbers. |
goToNextPage | () => void | Returned | Moves to the next visible page when one exists. |
goToPreviousPage | () => void | Returned | Moves to the previous visible page when one exists. |
setCurrentPage | Dispatch<SetStateAction<number>> | Returned | Sets current page state directly; invalid pages are corrected by useMultiPage effect. |
isFirstPage | boolean | true | True when currentPage is at visiblePages index 0. |
isLastPage | boolean | true | True when currentPage is at the final visiblePages index. |
progressValue | number | 100 | Progress percentage from useMultiPage. |
saveToStorage | () => void | Returned | Writes current values, timestamp, and currentPage through configured persistence. |
loadFromStorage | () => PersistedFormPayload<TFormValues> | undefined | Returned | Loads storage, sets form values, restores currentPage when stored currentPage is <= totalPages, and returns the parsed payload. |
clearStorage | () => void | Returned | Removes 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.
| Property | Type | Default | Description |
|---|---|---|---|
className | string | undefined | Class name for the native form element. |
aria-label | string | undefined | Accessible name when the form lacks a linked visible heading. |
aria-labelledby | string | undefined | References visible text that names the form. |
id | string | undefined | Native form id. |
name | string | undefined | Native form name. |
autoComplete | string | undefined | Native autocomplete mode for the form. |
noValidate | boolean | false | Native flag that disables browser validation UI. |
data-* | string | number | boolean | undefined | undefined | Custom data attributes forwarded to the native form. |
onBlur / onFocus | FocusEventHandler<HTMLFormElement> | undefined | Native focus callbacks, followed by onFormBlur and onFormFocus from hook config. |
onInput / onInvalid | FormEventHandler<HTMLFormElement> | undefined | Native input and invalid callbacks, followed by onFormInput and onFormInvalid. |
onKeyDown / onKeyUp | KeyboardEventHandler<HTMLFormElement> | undefined | Native keyboard callbacks, followed by onFormKeyDown and onFormKeyUp. |
onReset | FormEventHandler<HTMLFormElement> | undefined | Native reset callback, followed by formOptions.onReset, onFormReset, and analytics reset. |
onSubmit | FormEventHandler<HTMLFormElement> | Handled by Formedible | The 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.jsonTyped 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 />;
}