Skip to content

Forms

Compose labelled fields with one owner for values, validation and submission.

Choose validation ownership

Ghost Form wraps Base UI Form 1.8.0. Field associates the control, label, description and error; Field.Control is Ghost Input. Neither component creates a TanStack Form store, which holds values and validation state. For a small native form, use those controls and Base UI validation directly; see Form and Field.

Use TanStack Form when the application needs typed values, coordinated validation or form state subscriptions. The optional composition below has been checked with @tanstack/react-form@1.33.5. It binds real hook state to the existing Ghost owners; there is no Ghost form engine or required TanStack Form package peer.

First TanStack Form integration

Follow installation for the package and shared component styles. Install @tanstack/react-form@1.33.5 in the consuming app. Pass an application save function that resolves after persistence or rejects on failure.

project-form.tsx

form.Field subscribes to one named value; Ghost Field.Root supplies the accessible field composition. Capture-phase submission prevents the Base UI submit validator from becoming a second gate. noValidate disables native submit validation. The handler checks current submission state because repeated submit events can still reach a form whose button is disabled. TanStack owns the validator and submission; do not add Field.validate or a second schema validator to this recipe. Field.Error match displays the external error and links it to the control.

Validation and application effects

The validator returns undefined for a valid value and a message for an invalid one. TanStack reruns the relevant validation on submission. Validate on the server too: a typed client form is not a trust boundary. Use schema or async validators through TanStack validation; normalize schema error objects to messages before rendering them.

The host owns save requests, authorization, server field errors and successful navigation. Keep input after a failed save; do not reset optimistically. Disable duplicate submits while pending. For a server field error, map it to TanStack's field error state or a separately owned error record with a clear clearing/revalidation rule. Do not inject it into a competing Base UI validation flow.

Compose and extend

Use form.Subscribe for derived submit state instead of reading a snapshot outside a subscription. Bind custom controls through their value callback and blur event; do not spread text-input props onto a Select or Checkbox. Keep field components stable so edits do not remount controls. For repeated application forms, TanStack's createFormHook and createFormHookContexts can bind your own field components while retaining inference; an application factory does not belong in Ghost core.

Use Fieldset to name a related group. Labels, layout and styling remain on the component pages. For exact hooks, types, field paths, arrays and reset behavior, use TanStack Form; for Ghost props, use Form API and Field API.

Troubleshooting

If submit never reaches your handler, check for competing native/Base UI validation and confirm the capture handler calls handleSubmit. If values remain frozen, supply both the current value and the control's correct change callback. If an error is invisible, ensure invalid and Field.Error match use the same external error state. Default values initialize the store; use the engine's reset/update API deliberately when switching records, and protect unsaved input from late fetches.