FAQs
Use these answers to understand the boundaries between your application, React Hook Form (RHF), Standard Schema, and Form, Please.
Validation and submission
Why does a valid submit parse the schema once?
The validation parse also returns the transformed output. See The schema parses once on submit.
What does onSubmit receive?
It receives value, input, form, and submitter. See onSubmit receives
four values.
Does submission use native FormData?
No. Submission uses the values in React Hook Form state. Nested objects, arrays,
and File values keep their JavaScript types.
Your application owns transport serialization. Send JSON, construct
FormData, or use another protocol in onSubmit. Form, Please does not
select a transport format.
const uploadSchema = z.object({
title: z.string().trim().min(1),
attachment: z.file().optional(),
})
function createUploadBody(value: FormOutput<typeof uploadSchema>): FormData {
const body = new FormData()
body.set("title", value.title)
if (value.attachment !== undefined) {
body.set("attachment", value.attachment)
}
return body
}Pass the returned object as the fetch body. Do not set the content-type
header because the browser must add the multipart boundary.
Does required: true add a validation rule?
No. The Standard Schema owns all validation rules. See required is
UI-only.
How do I display server errors?
Keep request failures in application state, and apply known field issues with
form.api.setError. See Server issues are not schema
issues.
Conditional UI and values
Do hidden fields lose their values?
No. A hidden field keeps its value, and the schema still validates it. See Hidden fields keep their values.
Does Form, Please fetch async options?
Yes. A selectable field can use an async options function. Form, Please
invokes it, tracks the values and context properties it reads, aborts the
previous signal when those dependencies change, and ignores stale results.
The function still contains the application request:
options: async ({ values, context, signal }) =>
loadCities(values.country, context.locale, signal)The built-in policy uses [] during loading and after an uncaught error. It
does not provide caching, retries, loading UI, error UI, search, or pagination.
Use application resource state and fromResource or matchResource when you
need those policies. A successfully resolved non-array value is a contract error,
not an empty collection. See Resource state.
Can a UI resolver return a promise?
Ordinary UI resolvers must return immediately. A promise-like result causes a
runtime error. A selectable field's options function is the deliberate
exception and may return a promise.
Load data before or beside the form. Pass the current result through context. Resolvers can then derive labels, descriptions, control props, visibility, and interaction state synchronously.
Form, Please resolves the complete UI tree after each form value change. Keep resolvers small and free of side effects.
React Hook Form and manual UI
How do I use React Hook Form directly?
Use the API on form.api. This API is the typed React Hook Form instance that
owns the editable state.
Use register, Controller, or useController for custom fields. Use
useWatch, useFormState, or useFieldArray for derived and array UI. Keep
generated and manual UI inside the same kit.Form; it provides RHF
FormProvider automatically.
You can also call React Hook Form methods such as setValue and reset
through form.api. See Compose generated and custom
UI for a complete example.
Generated controls and form.update pass through configured value middleware.
Direct form.api calls, reset, and initial values do not. Use raw RHF methods
as an explicit escape hatch rather than expecting middleware to intercept them.
See Value middleware.
Should I create a second form for custom UI?
No. Create one binding with kit.useForm. Put kit.Fields and custom RHF
components inside the same kit.Form.
A second binding creates a second state owner. Changes, validation state, and submission state do not automatically synchronize between two bindings.
Can I migrate an existing React Hook Form app?
Yes. Migrate one form at a time. RHF forms and Form, Please forms work in the
same application. A large form can move in steps, because form.api keeps the
existing RHF field code working. Read Migrate from React Hook
Form.
What happens after an invalid submit?
RHF focuses the first registered invalid field. Read Focus the first issue.
Definitions, kits, and lifetime
Can I replace a definition while the component stays mounted?
No. kit.useForm keeps the first definition for the complete hook lifetime.
Passing a different definition prop does not replace it.
Define stable definitions at module scope when possible. To load another
document or definition, remount the editor with a React key. Use
form.api.reset(nextInput) when the definition stays the same and you want to
install a new editable baseline deliberately. See Load an edit-form
baseline for both operations.
Can a definition use a different form kit?
No. A definition belongs to the exact kit that created it. Pass it back to
useForm on that kit.
kit.forContext<Context>() is a type-only view of the same runtime kit. Use it
to type resolver context and require a context value in kit.useForm.
const profileKit = kit.forContext<ProfileContext>()When the application supplies a new context value, generated UI resolves with the current context and current form values.
Can I add controls or slots to an existing kit?
No. createFormKit takes one controls, slots, and grid snapshot. It freezes
that configuration.
Create the complete registries before you call createFormKit. Create another
kit when the application needs a different registry or grid.
Which schema libraries can I use?
Use any schema that implements Standard Schema. Form, Please does not require a library-specific adapter.
The documentation includes complete examples for Zod,
Yup, and Valibot. Field paths
and controls always use the schema input type. onSubmit.value uses the schema
output type.