Skip to content
Form, Please

TypeScript

Import the root types on this page from form-please.

import type { FieldPath, FormInput, FormOutput } from "form-please"

This page covers every type exported from each entry point. A section for a subpath entry names its import path, for example form-please/history. Import the types in that section from that path.

For functions, components, options, and runtime behavior, read the API reference.

Choose the type you need

Start with these types in application code:

TaskType
Type form state and defaultValues.FormInput<Schema>
Type a successful submit result.FormOutput<Schema>
Accept a valid field name.FieldPath<Value>
Type a reusable form fragment.FormFragment<Schema, Controls, ...>
Type a custom control component.ControlProps<Value, OwnProps, Context, Option>
Type form-local value middleware.FormMiddleware<Input, Context>

Most calls to defineForm, useForm, and createFormKit infer the remaining types. Add an explicit type when you export reusable configuration or build a library integration.

Schema input and output

StandardSchema<Input, Output> is the common schema contract. Form, Please works with any library that implements this contract.

FormInput<Schema> is the schema input type that React Hook Form (RHF) stores. FormOutput<Schema> is the parsed value passed to onSubmit. These types can be different when the schema transforms data.

type ProfileInput = FormInput<typeof profileSchema>
// { name: string; age: string; contacts: { email: string }[] }
 
type ProfileOutput = FormOutput<typeof profileSchema>
// { name: string; age: number; contacts: { email: string }[] }
 
const defaultValues = {
	name: "Ada Lovelace",
	age: "36",
	contacts: [{ email: "ada@example.com" }],
} satisfies ProfileInput

Use the input type for defaultValues, field paths, controls, and resolvers. Use the output type after successful submission. Do not use the output type to describe form state.

TypePurpose
StandardSchema<Input, Output>Describes a Standard Schema validator. Output defaults to Input.
FormInput<Schema>Extracts the schema input type.
FormOutput<Schema>Extracts the schema output type.
DeepReadonly<Value>Recursively models a readonly value. Functions, dates, files, and React elements keep their types. It does not freeze data at runtime.

Field paths

Path types use the schema input shape and React Hook Form path syntax. Array indexes use dot segments, such as contacts.0.email.

const contactEmailPath = "contacts.0.email" satisfies FieldPath<ProfileInput>
const contactsPath = "contacts" satisfies ArrayFieldPath<ProfileInput>
 
type ContactEmail = PathValue<ProfileInput, "contacts.0.email">
// string
 
type FieldUpdate<Value, Path extends FieldPath<Value>> = {
	readonly path: Path
	readonly value: PathValue<Value, Path>
}
 
const emailUpdate = {
	path: "contacts.0.email",
	value: "grace@example.com",
} satisfies FieldUpdate<ProfileInput, "contacts.0.email">
TypeResult
FieldPath<Value>All valid string paths in Value.
PathValue<Value, Path>The value at one valid path.
ArrayFieldPath<Value>RHF object-array paths that cannot be null or undefined.

Use FieldPath when a component or helper accepts any field. Add a second path generic when its value type must stay linked to the selected path. The FieldUpdate type in the example keeps this relationship.

Definition and UI types

kit.defineForm() accepts a UI tree plus an optional managed-update policy and returns a normalized FormDefinition. Let the call infer the type for a definition that stays in one file.

The factory builder types each node from the schema and kit. The tree needs no type annotation:

const profileDefinition = profileKit.defineForm(profileSchema, (ui) => [
	ui.field("name", {
		control: "text",
		label: "Name",
		readOnly: (_values, { context }) => !context.canEdit,
	}),
	ui.array("contacts", {
		label: "Contacts",
		itemDefault: { email: "" },
		children: (contact) => [
			contact.field("email", { control: "text", label: "Email" }),
		],
	}),
])

The node types below carry the schema input, control registry, context, slot configuration, selectable options, and grid values through the complete tree. Use defineFragment instead of a separate node list to share UI.

TypePurpose
UiNode<Input, Controls, ...>Union of ordinary field, section, array, and render source nodes.
FieldNode<Input, Controls, ...>Field node with compatible paths, controls, props, and selectable options.
SectionNode<Input, Controls, ...>Section node with typed child nodes.
ArrayNode<Input, Controls, ...>Array node for one non-nullable object-array path.
RenderNode<Input, Context>React component node with resolved interaction state.
FormFragment<Schema, Controls, ...>Schema-owned UI fragment created by defineFragment.

FormDefinition<Schema, Controls, ...> is the normalized, readonly result of defineForm. It contains the schema, grid, and normalized nodes. Do not create this object manually. Its generic parameters preserve the kit contracts.

Resolver types

A resolvable UI property accepts a static value or a synchronous resolver. Ordinary form resolvers receive the complete schema input and { context }. Fragment-authored resolvers receive the local fragment input instead.

type Visibility = UiResolver<boolean, ProfileInput, ProfileContext>
 
const canShowProfile: Visibility = (values, { context }) =>
  context.canEdit && values.name.length > 0
TypePurpose
UiResolver<Result, Input, Context>Synchronous function that returns a derived UI value.
UiResolverValues<Input>Deeply readonly resolver values.
UiResolverDetails<Context>Object that contains deeply readonly context.
Resolvable<Value, Input, Context>Static Value or a UiResolver that returns it.
ErrorDisplay"all" or "first" for the errorDisplay option of a kit, form, field, or array.
WhenHidden<Value>"keep", "reset", or { value } for the field whenHidden property.
ReactUiContentA React element or string accepted by UI content properties.

Resolvable excludes a static function when Value is callable. This rule prevents Form, Please from confusing a component with a resolver.

DefaultGridValue is the default grid union: 1 | 2 | 3 | 4. A custom kit infers its grid union from createFormKit({ grid }).

Render-node types

RenderNodeComponent is a React component that receives RenderNodeProps. The props contain only disabled and readOnly. Use RenderNode<Input, Context> to type the complete node.

Control types

defineControl<Value, OwnProps, Context, Option>() returns a typed ControlDefinition. The value type controls which schema fields can use the control. The context type controls which form kits can use it.

function MoneyControl({
	value,
	setValue,
	blur,
	input,
	meta,
	props: moneyProps,
	context,
	disabled,
	readOnly,
	required,
}: ControlProps<number | undefined, MoneyProps, MoneyContext>) {
	return (
		<>
			<input
				aria-describedby={input["aria-describedby"]}
				aria-invalid={meta.invalid || undefined}
				disabled={disabled}
				id={input.id}
				lang={context.locale}
				min={moneyProps.min}
				name={input.name}
				onBlur={blur}
				onChange={(event) => {
					const nextValue = event.currentTarget.valueAsNumber
					if (Number.isNaN(nextValue)) {
						setValue(undefined)
						return
					}
					setValue(nextValue)
				}}
				readOnly={readOnly}
				ref={input.ref}
				required={required}
				type="number"
				value={value ?? ""}
			/>
			<span aria-hidden="true">{moneyProps.currencyLabel}</span>
		</>
	)
}
 
const moneyInput = {
	component: MoneyControl,
} satisfies DefineControlInput<
	number | undefined,
	MoneyProps,
	MoneyContext,
	never
>
 
const money = defineControl<number | undefined, MoneyProps, MoneyContext>(
	moneyInput,
)
 
type MoneyValue = ControlValueOf<typeof money> // number | undefined
type MoneyControlOwnProps = ControlOwnPropsOf<typeof money> // MoneyProps
type RequiredContext = ControlContextOf<typeof money> // MoneyContext
TypePurpose
ControlProps<Value, OwnProps, Context, Option>Props passed to a custom control component.
ControlDefinition<Value, OwnProps, Context, Option>Typed control returned by defineControl.
DefineControlInput<Value, OwnProps, Context, Option>Input object accepted by defineControl.
OptionValue<Value>Marks a control option value for narrowing to the selected schema input path.
FieldOptionsSource<Option, Input, Context>Static options or a tracked options function.
FieldOptionsResolver<Option, Input, Context>Function that returns options synchronously or asynchronously.
FieldOptionsResolverDetails<Input, Context>Readonly values, context, and cancellation signal.
ControlDefinitionRegistryReadonly record of named control definitions.
AnyControlDefinitionNon-specific control type used in registries.

Put OptionValue<Value> on the scalar option or on one property of a structured option. A field definition replaces the marker with the compatible schema input union at its path. The control receives OwnProps through props and the resolved collection through options.

See Option controls for a custom control and Option values for definition-side inference.

The extraction types read the hidden contract from a control definition:

TypeExtracts
ControlValueOf<Control>Field value type.
ControlOwnPropsOf<Control>Definition-node props type.
ControlOptionOf<Control>One selectable option item.
ControlContextOf<Control>Required form context type.

Use these extraction types for wrappers and integration tests. Application forms usually rely on inference.

Form-kit and binding types

A form kit is the reusable design-system contract. A form binding is one component-local form instance.

const formOptions = {
	defaultValues,
	context: { canEdit: true },
	onSubmit: ({ input, value }) => {
		input.age.toUpperCase() // string
		value.age.toFixed(0) // number
	},
} satisfies UseFormOptions<typeof profileSchema, ProfileContext>
 
function ProfileForm() {
	const form: FormBinding<typeof profileSchema, ProfileContext> =
		profileKit.useForm(profileDefinition, formOptions)
 
	return (
		<profileKit.AutoForm aria-label="Profile" form={form}>
			<profileKit.Submit>Save profile</profileKit.Submit>
		</profileKit.AutoForm>
	)
}
TypePurpose
FormKit<Controls, ...>Kit methods, components, controls, slots, grid, and context contract.
CreateFormKitOptions<Controls, ...>Options accepted by createFormKit.
DefineFormOptions<Schema, Context>Definition-owned beforeUpdate, ordered middleware, and afterUpdate.
FormDefinitionUpdatePolicy<Schema, Context>Normalized readonly managed-update policy stored by a definition.
UseFormOptions<Schema, Context>defaultValues, context, flags, validation modes, and submit handler.
FormSubmitDetails<Schema, Context>Parsed output, schema input, form binding, and submitter snapshot passed to a submit handler.
FormActionConfig<Schema, Context>One submit action: implicit, validate, and onSubmit.
FormActionsConfig<Schema, Context>The actions record accepted by useForm.
FormActionValidateDetails<Schema>Parsed output and schema input passed to an action validate.
FormAction<Schema, Context, Name>A bound action from form.actions.
FormSubmission<Name>External store of the running action and the last onSubmit rejection.
FormSubmissionSnapshot<Name>{ action, error } snapshot of FormSubmission. error is { value } or null.
FormIssueInput<Schema>An issue with a typed optional path for validate and setIssues.
SetIssuesOptions<Schema>The focus option of setIssues.
FormBinding<Schema, Context>Raw RHF API, managed update, fixed definition, and current context.
FormProps<Schema, Context>Props for kit.Form.

AutoFormProps<Schema, Context> is currently the same contract as FormProps. It is a separate public name for components that wrap kit.AutoForm.

Use form.api from FormBinding for unchanged React Hook Form operations. Use form.update for a managed value transaction. The other public fields are the fixed definition and current context. Read FormBinding on the API page for their behavior.

Managed value types

Use these types when you configure value middleware or create a reusable managed update:

TypePurpose
FormUpdateRecipe<Input>Synchronous Immer recipe accepted by form.update and middleware api.update.
FormMiddleware<Input, Context>Redux-shaped middleware for one form binding.
FormMiddlewareApi<Input>Read current values or start a later managed update.
FormMiddlewareNextForward authoritative patches to the next middleware or terminal.
ValueTransaction<Input, Context>TypeScript-readonly proposal or final committed update with previous values, next values, patches, source, and context. It is not a frozen snapshot.
ValueTransactionSource<Input>Discriminated source for a control, array action, form.update, or optional feature restore.
ValuePatchReadonly Immer operation with a segment-array path.

Middleware can replace the terminal return value. Therefore, form.update and FormMiddlewareNext return unknown. Read Value middleware for runtime rules and examples.

Managed value history types

Import history types from form-please/history:

TypePurpose
CreateHistoryOptionsConfigure retention and control grouping.
HistoryFeatureReusable middleware plus exact-form handle lookup.
HistoryHandle<Input>Navigate, subscribe, clear, export, and import one form's history.
HistorySnapshotCurrent canUndo, canRedo, index, and length.
HistoryOperationResultapplied, unavailable, cancelled, or transformed.
HistoryJournal<Input>Version 1 in-memory input positions and current index.
UseHistoryResult<Input>The useHistory return value: a HistoryHandle<Input> plus the current snapshot.

Read Managed value history for the complete runtime contract.

Form persistence types

Import persistence types from form-please/persistence:

TypePurpose
JsonValueValue accepted by an adapter across its JSON boundary.
PersistenceCodec<Value>Tagged conversion for one explicit opaque value type.
PersistenceMigrationConvert decoded input between application versions.
FormPersistenceAdapterKeyed asynchronous load, save, and remove transport.
CreatePersistenceOptionsConfigure the adapter, key, version, save delay, codecs, migration, and error observer.
PersistenceErrorDetailsIdentifies the failed restore, save, or clear operation.
PersistenceFeatureReusable middleware plus exact-form handle lookup.
PersistenceHandleRestore, start, flush, clear, and observe one form's draft.
PersistenceSnapshotCurrent restore phase and save state.
PersistenceRestoreResultapplied, empty, cancelled, transformed, or conflict.
UsePersistenceResultThe usePersistence return value: a PersistenceHandle plus the current snapshot.

Read Form persistence for the complete runtime contract.

Devtools types

Import the devtools type from form-please/devtools:

TypePurpose
FormPleaseDevtoolsProps<Schema, Context>Props for FormPleaseDevtools: the form binding to inspect and an optional name label.

Read Devtools for the runtime behavior.

Definition testing types

Import definition testing types from form-please/testing:

TypePurpose
DefinitionTester<Definition>Stateful selectors and production-backed definition operations.
DefinitionTesterOptions<Definition>Initial values, context, and interaction flags.
DefinitionInspection<Definition>Readonly values and stable public node projections for one resolution.
DefinitionTransition<Definition>Before, after, and exact public node changes.
ManagedDefinitionTransition<Definition>A definition transition plus commit status, final transaction, and middleware result.
DefinitionNodeSnapshot<Definition>Discriminated field, section, array, or render projection.
DefinitionChange<Definition>Added, removed, or property-level changed node.
DefinitionTestValues<Definition>Schema input inferred from the definition schema.
DefinitionTestContext<Definition>Runtime context inferred from the definition's kit.

DefinitionFieldSnapshot, DefinitionSectionSnapshot, DefinitionArraySnapshot, DefinitionRenderSnapshot, DefinitionPropertyChange, DefinitionTestOption, and DefinitionTesterRerenderOptions provide the narrower public contracts. Read Test definitions for runtime behavior.

Slot and structure types

FormKitSlots<FieldOptions, SectionOptions, ArrayOptions> describes the six required slot components. The three option generics set the corresponding slotOptions prop types.

function ErrorMessage({ rootProps, issue }: ErrorMessageSlotProps) {
	return (
		<p {...rootProps} role="alert">
			{issue.path !== undefined && `${issue.path}: `}
			{issue.message}
		</p>
	)
}
 
const controls = createNativeControls()
const slots = {
	...createDefaultSlots(),
	ErrorMessage,
} satisfies FormKitSlots
 
const kitOptions = {
	controls,
	slots,
	grid: [1, 2, 4],
} satisfies CreateFormKitOptions<typeof controls>
 
const customKit = createFormKit(kitOptions)
 
const formStyle = {
	"--fp-row-gap": "1rem",
} satisfies FormPleaseStyle
Slot props typeUsed by
FieldSlotProps<Options>Field slot.
SectionSlotProps<Options>Section slot.
ArraySlotProps<Options>Array slot.
ArrayItemSlotPropsArrayItem slot.
ErrorMessageSlotPropsErrorMessage slot.

SubmitSlotProps<Schema> types the Submit slot and typed render functions. It provides buttonProps, deeply readonly input values, isSubmitting, isDirty, and canSubmit (!isValidating && !isSubmitting).

Shared structure types

TypePurpose
StructuralRootPropsGenerated attributes, style, and ref for a structural root element.
StructuralNodeNameAllowed data-fp-node values for generated structure.
FormPleaseStyleReact CSS properties plus the supported --fp-* variables.
FormIssueNormalized validation issue with message and optional path.

Spread rootProps on the slot's root element. Do not replace its generated attributes or ref. Read Form kits for every slot prop.

Control and preset types

The control and preset entries export these helper types. Read the matching API sections for the factories and the control props types.

Native control types

Import native control types from form-please/native-controls:

TypePurpose
NativeSelectOption<Value>One select or radio option with a value, a text label, and an optional disabled flag.
NativeSelectEmptyOptionThe props.emptyOption entry that maps an empty HTML value to undefined.
NativeTextTypeThe HTML input types of the text control: text, email, password, search, tel, and url.

Read Native controls for the props type of each control.

Default slot types

Import default slot types from form-please/default-slots:

TypePurpose
DefaultSlotsI18nThe array action messages. Read Localization for the keys.
DefaultSlotI18nValue<Data>A fixed string, or a function that gets readonly Data and returns a string.
DefaultArrayAddI18nDataThe arrayAdd data: the optional label of the array node.
DefaultArrayItemI18nDataThe item action data: the zero-based index and the one-based position.

Read Default slots for the i18n option.

Material UI preset types

Import Material UI preset types from form-please/preset-mui:

TypePurpose
CreateMuiFormKitOptionsOptions for createMuiFormKit: an optional partial i18n object.
MuiFormKitI18nThe messages addItem, removeItem, moveItemUp, moveItemDown, and chooseFile.
MuiSelectOptionOne select or select-multiple option with a value, a label, and an optional disabled flag.
MuiRadioOptionOne radio option. It adds optional radioProps and labelProps to the select option fields.
MuiAutocompleteTextFieldPropsThe textFieldProps of the autocomplete and autocomplete-multiple controls.
MuiFieldSlotOptionsField slotOptions: sx for the FormControl root.
MuiSectionSlotOptionsSection slotOptions: sx for the section root and layoutSx for its grid.
MuiArraySlotOptionsArray slotOptions: sx for the array root and itemsSx for its item list.

Read Material UI preset for the control names and their props types.

Resource state

ResourceState<Value, Error> is a readonly tagged union. Its status is "pending", "success", or "error".

type Countries = ResourceState<readonly string[], Error>
 
function countryCount(resource: Countries) {
	if (resource.status !== "success") return 0
	return resource.value.length
}

The type does not fetch or cache data. Use it to expose application-owned resource state. Read Resource state for mapping and adapter examples.

Array reconciliation

FormBinding.array infers its row type from an ArrayFieldPath. Its reconcile method checks string or number keys, the key callback, and complete rows returned by create. See Reconcile selected rows.