TypeScript
Import the types on this page from form-please.
import type { FieldPath, FormInput, FormOutput } from "form-please"This page covers every type exported from the root entry point. Types for native controls, default slots, and Material UI use their documented subpath entries.
For functions, components, options, and runtime behavior, read the API reference.
Choose the type you need
Start with these types in application code:
| Task | Type |
|---|---|
Type form state and defaultValues. | FormInput<Schema> |
| Type a successful submit result. | FormOutput<Schema> |
| Accept a valid field name. | FieldPath<Value> |
| Type shared form UI. | UiNode<Input, Controls, ...> |
| 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 editable value stored in React Hook Form.
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 ProfileInputUse 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.
| Type | Purpose |
|---|---|
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. 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">| Type | Result |
|---|---|
FieldPath<Value> | All valid string paths in Value. |
PathValue<Value, Path> | The value at one valid path. |
ArrayFieldPath<Value> | RHF field-array paths whose items are objects. |
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 and returns a normalized
FormDefinition. Let the call infer the type for a definition that stays in
one file.
Use UiNode when the UI tree is declared separately:
type ProfileNode = UiNode<
ProfileInput,
typeof profileKit.controls,
ProfileContext
>
const profileUi = [
{
kind: "field",
path: "name",
control: "text",
label: "Name",
readOnly: (_values, { context }) => !context.canEdit,
},
{
kind: "array",
path: "contacts",
label: "Contacts",
itemDefault: { email: "" },
children: [
{
kind: "field",
path: "email",
control: "text",
label: "Email",
},
],
},
] satisfies readonly ProfileNode[]
const profileDefinition = profileKit.defineForm(profileSchema, {
ui: profileUi,
})The node generics carry the schema input, control registry, context, slot configuration, selectable options, and grid values through the complete tree.
| Type | Purpose |
|---|---|
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 whose child paths are relative to one item. |
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| Type | Purpose |
|---|---|
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. |
ReactUiContent | A 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| Type | Purpose |
|---|---|
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. |
ControlDefinitionRegistry | Readonly record of named control definitions. |
AnyControlDefinition | Non-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:
| Type | Extracts |
|---|---|
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>
)
}| Type | Purpose |
|---|---|
FormKit<Controls, ...> | Kit methods, components, controls, slots, grid, and context contract. |
CreateFormKitOptions<Controls, ...> | Options accepted by createFormKit. |
UseFormOptions<Schema, Context> | defaultValues, context, flags, managed update hooks, value middleware, and submit handler. |
FormSubmitDetails<Schema, Context> | Parsed output, editable input, form binding, and submitter snapshot passed to a submit handler. |
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:
| Type | Purpose |
|---|---|
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. |
FormMiddlewareNext | Forward 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. |
ValuePatch | Readonly 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:
| Type | Purpose |
|---|---|
CreateHistoryOptions | Configure retention and control grouping. |
HistoryFeature | Reusable middleware plus exact-form handle lookup. |
HistoryHandle<Input> | Navigate, subscribe, clear, export, and import one form's history. |
HistorySnapshot | Current canUndo, canRedo, index, and length. |
HistoryOperationResult | applied, unavailable, cancelled, or transformed. |
HistoryJournal<Input> | Version 1 in-memory input positions and current index. |
Read Managed value history for the complete runtime contract.
Form persistence types
Import persistence types from form-please/persistence:
| Type | Purpose |
|---|---|
JsonValue | Value accepted by an adapter across its JSON boundary. |
PersistenceCodec<Value> | Tagged conversion for one explicit opaque value type. |
PersistenceMigration | Convert decoded input between application versions. |
FormPersistenceAdapter | Keyed asynchronous load, save, and remove transport. |
CreatePersistenceOptions | Configure the adapter, key, version, save delay, codecs, migration, and error observer. |
PersistenceErrorDetails | Identifies the failed restore, save, or clear operation. |
PersistenceFeature | Reusable middleware plus exact-form handle lookup. |
PersistenceHandle | Restore, start, flush, clear, and observe one form's draft. |
PersistenceSnapshot | Current restore phase and save state. |
PersistenceRestoreResult | applied, empty, cancelled, transformed, or conflict. |
Read Form persistence for the complete runtime contract.
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 type | Used by |
|---|---|
FieldSlotProps<Options> | Field slot. |
SectionSlotProps<Options> | Section slot. |
ArraySlotProps<Options> | Array slot. |
ArrayItemSlotProps | ArrayItem slot. |
ErrorMessageSlotProps | ErrorMessage 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
| Type | Purpose |
|---|---|
StructuralRootProps | Generated attributes, style, and ref for a structural root element. |
StructuralNodeName | Allowed data-fp-node values for generated structure. |
FormPleaseStyle | React CSS properties plus the supported --fp-* variables. |
FormIssue | Normalized 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.
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.