From c65a7f6296beff31533199ff32635438c43c8386 Mon Sep 17 00:00:00 2001 From: Maks Pikov Date: Tue, 10 Mar 2026 22:13:02 +0000 Subject: [PATCH] docs(react): add Getting and Setting Form Values guide Adds a new guide page that covers: - Initializing forms with static and async/API-fetched values (with and without TanStack Query) - Reading field values reactively (useStore, form.Subscribe) and imperatively (getFieldValue, form.state.values) - Setting field values programmatically (setFieldValue, field.setValue, form.reset) - Validation behaviour when setting values programmatically, and how to suppress or trigger it manually - Marking fields as touched alongside programmatic updates - Brief overview of the listeners API for side-effect-driven value changes, with links to the Reactivity and Listeners guides Closes #478 --- docs/config.json | 4 + .../guides/getting-and-setting-values.md | 264 ++++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 docs/framework/react/guides/getting-and-setting-values.md diff --git a/docs/config.json b/docs/config.json index 6b289ada6..100ceec5f 100644 --- a/docs/config.json +++ b/docs/config.json @@ -103,6 +103,10 @@ "label": "Basic Concepts", "to": "framework/react/guides/basic-concepts" }, + { + "label": "Getting and Setting Values", + "to": "framework/react/guides/getting-and-setting-values" + }, { "label": "Form Validation", "to": "framework/react/guides/validation" diff --git a/docs/framework/react/guides/getting-and-setting-values.md b/docs/framework/react/guides/getting-and-setting-values.md new file mode 100644 index 000000000..585bbf8aa --- /dev/null +++ b/docs/framework/react/guides/getting-and-setting-values.md @@ -0,0 +1,264 @@ +--- +id: getting-and-setting-values +title: Getting and Setting Form Values +--- + +This guide covers the different ways to read and write form values in TanStack Form—both at initialization time and at runtime. + +## Setting initial values + +The simplest way to populate a form is by passing `defaultValues` to `useForm`: + +```tsx +const form = useForm({ + defaultValues: { + firstName: 'John', + lastName: 'Doe', + age: 30, + }, + onSubmit: async ({ value }) => { + console.log(value) + }, +}) +``` + +You can also share defaults across multiple forms using `formOptions`: + +```tsx +import { formOptions, useForm } from '@tanstack/react-form' + +const sharedOptions = formOptions({ + defaultValues: { + firstName: '', + lastName: '', + }, +}) + +// Later, in a component: +const form = useForm({ + ...sharedOptions, + onSubmit: async ({ value }) => console.log(value), +}) +``` + +## Initializing with async / API data + +When your default values come from a server, you have two main options. + +### Without TanStack Query + +You can fetch the data yourself and pass it to `defaultValues` once it is ready. Until then, render a loading state: + +```tsx +export default function App() { + const [initialData, setInitialData] = React.useState<{ + firstName: string + lastName: string + } | null>(null) + + React.useEffect(() => { + fetch('/api/user') + .then((r) => r.json()) + .then((data) => setInitialData(data)) + }, []) + + if (!initialData) return

Loading…

+ + return +} + +function UserForm({ + initialData, +}: { + initialData: { firstName: string; lastName: string } +}) { + const form = useForm({ + defaultValues: initialData, + onSubmit: async ({ value }) => console.log(value), + }) + // …render the form +} +``` + +> **Tip:** Always render the form *after* the data is ready so `defaultValues` is only evaluated once. Changing `defaultValues` after the form mounts has no effect. + +### With TanStack Query + +[TanStack Query](https://tanstack.com/query) pairs perfectly with TanStack Form for async defaults. See the dedicated [Async Initial Values](./async-initial-values.md) guide for a complete example. + +```tsx +import { useForm } from '@tanstack/react-form' +import { useQuery } from '@tanstack/react-query' + +export default function App() { + const { data, isLoading } = useQuery({ + queryKey: ['user'], + queryFn: () => fetch('/api/user').then((r) => r.json()), + }) + + if (isLoading) return

Loading…

+ + return +} +``` + +## Getting field values + +### Inside a field's render prop + +Within a `form.Field` render, the current value is available on `field.state.value`: + +```tsx + + {(field) => ( + field.handleChange(e.target.value)} + /> + )} + +``` + +### Imperatively (non-reactive) + +Call `form.getFieldValue(fieldName)` anywhere outside a render prop to read the **current snapshot** of a field value. This does **not** subscribe to future updates: + +```tsx +const currentName = form.getFieldValue('firstName') +``` + +To read the entire form state at once, access `form.state.values`: + +```tsx +const allValues = form.state.values +``` + +### Reactively + +If you need a value that **re-renders your component** when it changes, use `useStore` from the store package: + +```tsx +import { useStore } from '@tanstack/react-store' + +const firstName = useStore(form.store, (state) => state.values.firstName) +``` + +To keep re-renders scoped to a sub-tree of your UI rather than the full component, use the `form.Subscribe` component: + +```tsx + state.values.age}> + {(age) =>

You are {age} years old.

} +
+``` + +> See the [Reactivity](./reactivity.md) guide for a deeper dive into `useStore` vs `form.Subscribe`. + +## Setting field values programmatically + +### Setting a single field + +Use `form.setFieldValue` to update one field from outside a render prop—for example in response to a button click or an external event: + +```tsx +// Set a scalar value +form.setFieldValue('firstName', 'Jane') + +// Use an updater function for derived updates +form.setFieldValue('age', (prev) => prev + 1) +``` + +Inside a field's render prop, prefer `field.setValue` (or `field.handleChange`) instead: + +```tsx + + {(field) => ( + + )} + +``` + +### Resetting the form to new values + +To replace **all** field values at once (and optionally update the stored default values), call `form.reset`: + +```tsx +// Reset to completely new values +form.reset({ + firstName: 'Jane', + lastName: 'Smith', + age: 25, +}) +``` + +`form.reset` also re-initialises the form's "dirty" and "touched" tracking, making it ideal for switching between records in a master/detail view. + +### Setting values across multiple fields individually + +If you need to set several fields without resetting the form state, call `setFieldValue` multiple times: + +```tsx +form.setFieldValue('firstName', 'Jane') +form.setFieldValue('lastName', 'Smith') +``` + +## Programmatic updates and validation + +By default, calling `form.setFieldValue` or `field.setValue` **does** run the field's change-event validators (the same validators that run when the user types). This is equivalent to how the value changes when a user interacts with the input. + +### Skipping validation + +To update a value without triggering validation, pass `{ dontValidate: true }` in the options: + +```tsx +form.setFieldValue('firstName', 'Jane', { dontValidate: true }) +``` + +### Triggering validation manually + +To run validation on a field without changing its value, call `form.validateField`: + +```tsx +// Validate as if the field just changed +await form.validateField('firstName', 'change') + +// Validate as if the field was blurred +await form.validateField('firstName', 'blur') +``` + +### Marking a field as touched when setting its value + +Validators often only run after a field has been touched. If you set a value programmatically and also want to mark the field as touched (so error messages appear), call `field.setMeta` alongside the value update: + +```tsx + + {(field) => ( + + )} + +``` + +## Listening for value changes + +For **side effects** that should fire when a field changes (e.g. resetting a dependent field), use the listeners API: + +```tsx + { + form.setFieldValue('province', '') + }, + }} +> + {(field) => field.handleChange(e.target.value)} />} + +``` + +> See the [Side effects for event triggers](./listeners.md) guide for more detail, including built-in debouncing and form-level listeners.