+
### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
+
+
+# TanStack Form
+
+A headless form library for managing complex form state with full control over fields, validation, and workflows across any framework.
+
+- Framework‑agnostic & headless — bring your own UI
+- Fully typed with TypeScript
+- Reactive hooks & extensible modular architecture
+- Sync & async validation with debouncing and nested fields
+
+### Read the docs →
+
+
+
+## Get Involved
+
+- We welcome issues and pull requests!
+- Participate in [GitHub discussions](https://github.com/TanStack/form/discussions)
+- Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ)
+- See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions
+
+## Partners
+
+
+We're looking for TanStack Form Partners to join our mission! Partner with us to push the boundaries of TanStack Form and build amazing things together.
+
+
+
+
+## Explore the TanStack Ecosystem
+
+- TanStack Config – Tooling for JS/TS packages
+- TanStack DB – Reactive sync client store
+- TanStack DevTools – Unified devtools panel
+- TanStack Pacer – Debouncing, throttling, batching
+- TanStack Query – Async state & caching
+- TanStack Ranger – Range & slider primitives
+- TanStack Router – Type‑safe routing, caching & URL state
+- TanStack Start – Full‑stack SSR & streaming
+- TanStack Store – Reactive data store
+- TanStack Table – Headless datagrids
+- TanStack Virtual – Virtualized rendering
+
+… and more at TanStack.com »
-
+
diff --git a/codecov.yml b/codecov.yml
index 6cc665ac5..7700cbe30 100755
--- a/codecov.yml
+++ b/codecov.yml
@@ -6,3 +6,6 @@ coverage:
default:
target: auto
threshold: 1%
+ignore:
+ - 'packages/form-devtools'
+ - 'packages/react-form-devtools'
diff --git a/docs/assets/field-states-extended.png b/docs/assets/field-states-extended.png
index 033fe6e68..1ecc677af 100644
Binary files a/docs/assets/field-states-extended.png and b/docs/assets/field-states-extended.png differ
diff --git a/docs/assets/field-states.png b/docs/assets/field-states.png
index a78ed16f9..869f41f5c 100644
Binary files a/docs/assets/field-states.png and b/docs/assets/field-states.png differ
diff --git a/docs/assets/react_form_composability.svg b/docs/assets/react_form_composability.svg
index fd2f7ae14..9761825c5 100644
--- a/docs/assets/react_form_composability.svg
+++ b/docs/assets/react_form_composability.svg
@@ -1,3 +1,3 @@
-
\ No newline at end of file
+
diff --git a/docs/config.json b/docs/config.json
index 795815914..42228fec6 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -102,6 +102,10 @@
"label": "Form Validation",
"to": "framework/react/guides/validation"
},
+ {
+ "label": "Dynamic Validation",
+ "to": "framework/react/guides/dynamic-validation"
+ },
{
"label": "Async Initial Values",
"to": "framework/react/guides/async-initial-values"
@@ -134,6 +138,10 @@
"label": "UI Libraries",
"to": "framework/react/guides/ui-libraries"
},
+ {
+ "label": "Focus Management",
+ "to": "framework/react/guides/focus-management"
+ },
{
"label": "Form Composition",
"to": "framework/react/guides/form-composition"
@@ -149,6 +157,10 @@
{
"label": "Debugging",
"to": "framework/react/guides/debugging"
+ },
+ {
+ "label": "Devtools",
+ "to": "framework/react/guides/devtools"
}
]
},
@@ -163,6 +175,10 @@
"label": "Form Validation",
"to": "framework/vue/guides/validation"
},
+ {
+ "label": "Dynamic Validation",
+ "to": "framework/vue/guides/dynamic-validation"
+ },
{
"label": "Async Initial Values",
"to": "framework/vue/guides/async-initial-values"
@@ -188,9 +204,17 @@
"label": "Form Validation",
"to": "framework/angular/guides/validation"
},
+ {
+ "label": "Dynamic Validation",
+ "to": "framework/angular/guides/dynamic-validation"
+ },
{
"label": "Arrays",
"to": "framework/angular/guides/arrays"
+ },
+ {
+ "label": "Form Composition",
+ "to": "framework/angular/guides/form-composition"
}
]
},
@@ -205,6 +229,10 @@
"label": "Form Validation",
"to": "framework/solid/guides/validation"
},
+ {
+ "label": "Dynamic Validation",
+ "to": "framework/solid/guides/dynamic-validation"
+ },
{
"label": "Async Initial Values",
"to": "framework/solid/guides/async-initial-values"
@@ -216,6 +244,10 @@
{
"label": "Linked Fields",
"to": "framework/solid/guides/linked-fields"
+ },
+ {
+ "label": "Form Composition",
+ "to": "framework/solid/guides/form-composition"
}
]
},
@@ -226,6 +258,14 @@
"label": "Basic Concepts",
"to": "framework/lit/guides/basic-concepts"
},
+ {
+ "label": "Form Validation",
+ "to": "framework/lit/guides/validation"
+ },
+ {
+ "label": "Dynamic Validation",
+ "to": "framework/lit/guides/dynamic-validation"
+ },
{
"label": "Arrays",
"to": "framework/lit/guides/arrays"
@@ -243,6 +283,10 @@
"label": "Form Validation",
"to": "framework/svelte/guides/validation"
},
+ {
+ "label": "Dynamic Validation",
+ "to": "framework/svelte/guides/dynamic-validation"
+ },
{
"label": "Async Initial Values",
"to": "framework/svelte/guides/async-initial-values"
@@ -515,6 +559,10 @@
"label": "Form Composition",
"to": "framework/react/examples/large-form"
},
+ {
+ "label": "Dynamic Validation",
+ "to": "framework/react/examples/dynamic"
+ },
{
"label": "TanStack Query Integration",
"to": "framework/react/examples/query-integration"
@@ -542,6 +590,10 @@
{
"label": "Field Errors From Form Validators",
"to": "framework/react/examples/field-errors-from-form-validators"
+ },
+ {
+ "label": "Devtools",
+ "to": "framework/react/examples/devtools"
}
]
},
@@ -555,6 +607,10 @@
{
"label": "Arrays",
"to": "framework/vue/examples/array"
+ },
+ {
+ "label": "Standard Schema",
+ "to": "framework/vue/examples/standard-schema"
}
]
},
@@ -568,6 +624,14 @@
{
"label": "Arrays",
"to": "framework/angular/examples/array"
+ },
+ {
+ "label": "Form Composition",
+ "to": "framework/angular/examples/large-form"
+ },
+ {
+ "label": "Standard Schema",
+ "to": "framework/angular/examples/standard-schema"
}
]
},
@@ -581,6 +645,14 @@
{
"label": "Arrays",
"to": "framework/solid/examples/array"
+ },
+ {
+ "label": "Form Composition",
+ "to": "framework/solid/examples/large-form"
+ },
+ {
+ "label": "Standard Schema",
+ "to": "framework/solid/examples/standard-schema"
}
]
},
@@ -591,9 +663,17 @@
"label": "Simple",
"to": "framework/lit/examples/simple"
},
+ {
+ "label": "Array",
+ "to": "framework/lit/examples/array"
+ },
{
"label": "UI Libraries",
"to": "framework/lit/examples/ui-libraries"
+ },
+ {
+ "label": "Standard Schema",
+ "to": "framework/lit/examples/standard-schema"
}
]
},
@@ -607,6 +687,10 @@
{
"label": "Arrays",
"to": "framework/svelte/examples/array"
+ },
+ {
+ "label": "Standard Schema",
+ "to": "framework/svelte/examples/standard-schema"
}
]
}
diff --git a/docs/framework/angular/guides/basic-concepts.md b/docs/framework/angular/guides/basic-concepts.md
index 551d34b12..8527075e5 100644
--- a/docs/framework/angular/guides/basic-concepts.md
+++ b/docs/framework/angular/guides/basic-concepts.md
@@ -40,21 +40,51 @@ Each field has its own state, which includes its current value, validation statu
Example:
-```tsx
+```ts
const {
value,
meta: { errors, isValidating },
} = field.state
```
-There are three field states can be very useful to see how the user interacts with a field. A field is _"touched"_ when the user clicks/tabs into it, _"pristine"_ until the user changes value in it, and _"dirty"_ after the value has been changed. You can check these states via the `isTouched`, `isPristine` and `isDirty` flags, as seen below.
+There are four states in the metadata that can be useful to see how the user interacts with a field:
-```tsx
-const { isTouched, isPristine, isDirty } = field.state.meta
+- _"isTouched"_, after the user changes the field or blurs the field
+- _"isDirty"_, after the field's value has been changed, even if it's been reverted to the default. Opposite of _"isPristine"_
+- _"isPristine"_, until the user changes the field value. Opposite of _"isDirty"_
+- _"isBlurred"_, after the field has been blurred
+
+```ts
+const { isTouched, isDirty, isPristine, isBlurred } = field.state.meta
```

+## Understanding 'isDirty' in Different Libraries
+
+Non-Persistent `dirty` state
+
+- **Libraries**: React Hook Form (RHF), Formik, Final Form.
+- **Behavior**: A field is 'dirty' if its value differs from the default. Reverting to the default value makes it 'clean' again.
+
+Persistent `dirty` state
+
+- **Libraries**: Angular Form, Vue FormKit.
+- **Behavior**: A field remains 'dirty' once changed, even if reverted to the default value.
+
+We have chosen the persistent 'dirty' state model. To also support a non-persistent 'dirty' state, we introduce an additional flag:
+
+- _"isDefaultValue"_, whether the field's current value is the default value
+
+```ts
+const { isDefaultValue, isTouched } = field.state.meta
+
+// The following line will re-create the non-Persistent `dirty` functionality.
+const nonPersistentIsDirty = !isDefaultValue
+```
+
+
+
## Field API
The Field API is an object accessed in the `tanstackField.api` property when creating a field. It provides methods for working with the field's state.
@@ -125,9 +155,10 @@ You can define a schema using any of the libraries implementing the specificatio
Supported libraries include:
-- [Zod](https://zod.dev/)
-- [Valibot](https://valibot.dev/)
-- [ArkType](https://arktype.io/)
+- [Zod](https://zod.dev/) (v3.24.0 or higher)
+- [Valibot](https://valibot.dev/) (v1.0.0 or higher)
+- [ArkType](https://arktype.io/) (v2.1.20 or higher)
+- [Yup](https://github.com/jquense/yup) (v1.7.0 or higher)
Example:
@@ -228,7 +259,7 @@ onCountryChange: FieldListenerFn = ({
}
```
-More information can be found at [Listeners](./listeners.md)
+More information can be found at [Listeners](../listeners.md)
## Array Fields
diff --git a/docs/framework/angular/guides/dynamic-validation.md b/docs/framework/angular/guides/dynamic-validation.md
new file mode 100644
index 000000000..0ecff7434
--- /dev/null
+++ b/docs/framework/angular/guides/dynamic-validation.md
@@ -0,0 +1,297 @@
+---
+id: dynamic-validation
+title: Dynamic Validation
+---
+
+In many cases, you want to change the validation rules based depending on the state of the form or other conditions. The most popular
+example of this is when you want to validate a field differently based on whether the user has submitted the form for the first time or not.
+
+We support this through our `onDynamic` validation function.
+
+```angular-ts
+import { Component } from '@angular/core'
+import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+ `,
+})
+export class AppComponent {
+ form = injectForm({
+ defaultValues: {
+ firstName: '',
+ lastName: '',
+ },
+ // If this is omitted, onDynamic will not be called
+ validationLogic: revalidateLogic(),
+ validators: {
+ onDynamic: ({ value }) => {
+ if (!value.firstName) {
+ return { firstName: 'A first name is required' }
+ }
+ return undefined
+ },
+ },
+ })
+}
+```
+
+> By default `onDynamic` is not called, so you need to pass `revalidateLogic()` to the `validationLogic` option of `injectForm`.
+
+## Revalidation Options
+
+`revalidateLogic` allows you to specify when validation should be run and change the validation rules dynamically based on the current submission state of the form.
+
+It takes two arguments:
+
+- `mode`: The mode of validation prior to the first form submission. This can be one of the following:
+ - `change`: Validate on every change.
+ - `blur`: Validate on blur.
+ - `submit`: Validate on submit. (**default**)
+
+- `modeAfterSubmission`: The mode of validation after the form has been submitted. This can be one of the following:
+ - `change`: Validate on every change. (**default**)
+ - `blur`: Validate on blur.
+ - `submit`: Validate on submit.
+
+You can, for example, use the following to revalidate on blur after the first submission:
+
+```angular-ts
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+ `,
+})
+export class AppComponent {
+ form = injectForm({
+ // ...
+ validationLogic: revalidateLogic({
+ mode: 'submit',
+ modeAfterSubmission: 'blur',
+ }),
+ // ...
+ })
+}
+```
+
+## Accessing Errors
+
+Just as you might access errors from an `onChange` or `onBlur` validation, you can access the errors from the `onDynamic` validation function using the form's error map through `injectStore`.
+
+```angular-ts
+import { Component } from '@angular/core'
+import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
{{ formErrorMap().onDynamic?.firstName }}
+ `,
+})
+export class AppComponent {
+ form = injectForm({
+ // ...
+ validationLogic: revalidateLogic(),
+ validators: {
+ onDynamic: ({ value }) => {
+ if (!value.firstName) {
+ return { firstName: 'A first name is required' }
+ }
+ return undefined
+ },
+ },
+ })
+
+ formErrorMap = injectStore(this.form, (state) => state.errorMap)
+}
+```
+
+## Usage with Other Validation Logic
+
+You can use `onDynamic` validation alongside other validation logic, such as `onChange` or `onBlur`.
+
+```angular-ts
+import { Component } from '@angular/core'
+import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+
{{ formErrorMap().onChange?.firstName }}
+
{{ formErrorMap().onDynamic?.lastName }}
+
+ `,
+})
+export class AppComponent {
+ form = injectForm({
+ defaultValues: {
+ firstName: '',
+ lastName: '',
+ },
+ validationLogic: revalidateLogic(),
+ validators: {
+ onChange: ({ value }) => {
+ if (!value.firstName) {
+ return { firstName: 'A first name is required' }
+ }
+ return undefined
+ },
+ onDynamic: ({ value }) => {
+ if (!value.lastName) {
+ return { lastName: 'A last name is required' }
+ }
+ return undefined
+ },
+ },
+ })
+
+ formErrorMap = injectStore(this.form, (state) => state.errorMap)
+}
+```
+
+### Usage with Fields
+
+You can also use `onDynamic` validation with fields, just like you would with other validation logic.
+
+```angular-ts
+import { Component } from '@angular/core'
+import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
+import type { FieldValidateFn } from '@tanstack/angular-form'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+ `,
+})
+export class AppComponent {
+ ageValidator: FieldValidateFn = ({ value }) =>
+ value > 18 ? undefined : 'Age must be greater than 18'
+
+ form = injectForm({
+ defaultValues: {
+ name: '',
+ age: 0,
+ },
+ validationLogic: revalidateLogic(),
+ onSubmit({ value }) {
+ alert(JSON.stringify(value))
+ },
+ })
+
+ handleSubmit(event: SubmitEvent) {
+ event.preventDefault()
+ event.stopPropagation()
+ this.form.handleSubmit()
+ }
+}
+```
+
+### Async Validation
+
+Async validation can also be used with `onDynamic` just like with other validation logic. You can even debounce the async validation to avoid excessive calls.
+
+```angular-ts
+import { Component } from '@angular/core'
+import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+ `,
+})
+export class AppComponent {
+ form = injectForm({
+ defaultValues: {
+ username: '',
+ },
+ validationLogic: revalidateLogic(),
+ validators: {
+ onDynamicAsyncDebounceMs: 500, // Debounce the async validation by 500ms
+ onDynamicAsync: async ({ value }) => {
+ if (!value.username) {
+ return { username: 'Username is required' }
+ }
+ // Simulate an async validation
+ const isValid = await validateUsername(value.username)
+ return isValid ? undefined : { username: 'Username is already taken' }
+ },
+ },
+ })
+}
+```
+
+### Standard Schema Validation
+
+You can also use standard schema validation libraries like Valibot or Zod with `onDynamic` validation. This allows you to define complex validation rules that can change dynamically based on the form state.
+
+```angular-ts
+import { Component } from '@angular/core'
+import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
+import { z } from 'zod'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+ `,
+})
+export class AppComponent {
+ schema = z.object({
+ firstName: z.string().min(1, 'A first name is required'),
+ lastName: z.string().min(1, 'A last name is required'),
+ })
+
+ form = injectForm({
+ defaultValues: {
+ firstName: '',
+ lastName: '',
+ },
+ validationLogic: revalidateLogic(),
+ validators: {
+ onDynamic: this.schema,
+ },
+ })
+}
+```
diff --git a/docs/framework/angular/guides/form-composition.md b/docs/framework/angular/guides/form-composition.md
new file mode 100644
index 000000000..9bb32c7dd
--- /dev/null
+++ b/docs/framework/angular/guides/form-composition.md
@@ -0,0 +1,178 @@
+---
+id: form-composition
+title: Form Composition
+---
+
+A common criticism of TanStack Form is its verbosity out-of-the-box. While this _can_ be useful for educational purposes - helping enforce understanding our APIs - it's not ideal in production use cases.
+
+As a result, while basic usage of `[tanstackField]` enables the most powerful and flexible usage of TanStack Form, we provide APIs that wrap it and make your application code less verbose.
+
+## Pre-bound Field Components
+
+If you've ever used TanStack Form in Angular to bind more than one input, you'll have quickly realized how much goes into each input:
+
+```angular-ts
+import { Component } from '@angular/core'
+import { TanStackField, injectForm, injectStore } from '@tanstack/angular-form'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+ }
+ `,
+})
+export class AppTextField {
+ label = input.required()
+ // This API requires another part to it from the parent component
+ field = injectField()
+}
+```
+
+> `injectField` accepts a single generic to define the `field.state.value` type.
+>
+> As a result, a numerical text field would be represented as `injectField`, for example.
+
+Now, we can use the `TanStackAppField` directive (`tanstack-app-field`) to `provide` the expected field associated with this input:
+
+```angular-ts
+import { Component } from '@angular/core'
+import {
+ TanStackAppField,
+ TanStackField,
+ injectForm,
+} from '@tanstack/angular-form'
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField, TanStackAppField, AppTextField],
+ template: `
+
+
+
+
+
+
+ `,
+})
+export class AppComponent {
+ form = injectForm({
+ defaultValues: {
+ firstName: '',
+ lastName: '',
+ },
+ onSubmit({ value }) {
+ // Do something with form data
+ console.log(value)
+ },
+ })
+}
+```
+
+> Here, the `tanstack-app-field` directive is taking the properties from `[tanstackField]` and `provide`ing them down to the `app-text-field` so that they can be more easily consumed as a component.
diff --git a/docs/framework/angular/guides/submission-handling.md b/docs/framework/angular/guides/submission-handling.md
index e5fae2477..4f503f0cd 100644
--- a/docs/framework/angular/guides/submission-handling.md
+++ b/docs/framework/angular/guides/submission-handling.md
@@ -65,7 +65,7 @@ export class AppComponent {
## Transforming data with Standard Schemas
-While Tanstack Form provides [Standard Schema support](./validation.md) for validation, it does not preserve the Schema's output data.
+While Tanstack Form provides [Standard Schema support](../validation.md) for validation, it does not preserve the Schema's output data.
The value passed to the `onSubmit` function will always be the input data. To receive the output data of a Standard Schema, parse it in the `onSubmit` function:
diff --git a/docs/framework/angular/guides/validation.md b/docs/framework/angular/guides/validation.md
index 60e3a2dc8..85f384105 100644
--- a/docs/framework/angular/guides/validation.md
+++ b/docs/framework/angular/guides/validation.md
@@ -554,7 +554,7 @@ TanStack Form natively supports all libraries following the [Standard Schema spe
_Note:_ make sure to use the latest version of the schema libraries as older versions might not support Standard Schema yet.
-> Validation will not provide you with transformed values. See [submission handling](./submission-handling.md) for more information.
+> Validation will not provide you with transformed values. See [submission handling](../submission-handling.md) for more information.
To use schemas from these libraries you can pass them to the `validators` props as you would do with a custom function:
diff --git a/docs/framework/angular/reference/classes/tanstackappfield.md b/docs/framework/angular/reference/classes/tanstackappfield.md
new file mode 100644
index 000000000..c91b07bf7
--- /dev/null
+++ b/docs/framework/angular/reference/classes/tanstackappfield.md
@@ -0,0 +1,336 @@
+---
+id: TanStackAppField
+title: TanStackAppField
+---
+
+
+
+# Class: TanStackAppField\
+
+Defined in: [app-field.ts:20](https://github.com/TanStack/form/blob/main/packages/angular-form/src/app-field.ts#L20)
+
+## Extends
+
+- [`TanStackField`](../tanstackfield.md)\<`TParentData`, `TName`, `TData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TFormOnMount`, `TFormOnChange`, `TFormOnChangeAsync`, `TFormOnBlur`, `TFormOnBlurAsync`, `TFormOnSubmit`, `TFormOnSubmitAsync`, `TFormOnDynamic`, `TFormOnDynamicAsync`, `TFormOnServer`, `TSubmitMeta`\>
+
+## Type Parameters
+
+• **TParentData**
+
+• **TName** *extends* `DeepKeys`\<`TParentData`\>
+
+• **TData** *extends* `DeepValue`\<`TParentData`, `TName`\>
+
+• **TOnMount** *extends* `undefined` \| `FieldValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnChange** *extends* `undefined` \| `FieldValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnChangeAsync** *extends* `undefined` \| `FieldAsyncValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnBlur** *extends* `undefined` \| `FieldValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnBlurAsync** *extends* `undefined` \| `FieldAsyncValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnSubmit** *extends* `undefined` \| `FieldValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnSubmitAsync** *extends* `undefined` \| `FieldAsyncValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnDynamic** *extends* `undefined` \| `FieldValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnDynamicAsync** *extends* `undefined` \| `FieldAsyncValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TFormOnMount** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
+
+• **TFormOnChange** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
+
+• **TFormOnChangeAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
+
+• **TFormOnBlur** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
+
+• **TFormOnBlurAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
+
+• **TFormOnSubmit** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
+
+• **TFormOnSubmitAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
+
+• **TFormOnDynamic** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
+
+• **TFormOnDynamicAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
+
+• **TFormOnServer** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
+
+• **TSubmitMeta**
+
+## Constructors
+
+### new TanStackAppField()
+
+```ts
+new TanStackAppField(): TanStackAppField
+```
+
+Defined in: [app-field.ts:79](https://github.com/TanStack/form/blob/main/packages/angular-form/src/app-field.ts#L79)
+
+#### Returns
+
+[`TanStackAppField`](../tanstackappfield.md)\<`TParentData`, `TName`, `TData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TFormOnMount`, `TFormOnChange`, `TFormOnChangeAsync`, `TFormOnBlur`, `TFormOnBlurAsync`, `TFormOnSubmit`, `TFormOnSubmitAsync`, `TFormOnDynamic`, `TFormOnDynamicAsync`, `TFormOnServer`, `TSubmitMeta`\>
+
+#### Overrides
+
+[`TanStackField`](../tanstackfield.md).[`constructor`](../TanStackField.md#constructors)
+
+## Properties
+
+### \_api
+
+```ts
+_api: Signal>;
+```
+
+Defined in: [tanstack-field.ts:151](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L151)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`_api`](../TanStackField.md#_api)
+
+***
+
+### asyncAlways
+
+```ts
+asyncAlways: InputSignalWithTransform;
+```
+
+Defined in: [tanstack-field.ts:76](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L76)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`asyncAlways`](../TanStackField.md#asyncalways)
+
+***
+
+### asyncDebounceMs
+
+```ts
+asyncDebounceMs: InputSignalWithTransform;
+```
+
+Defined in: [tanstack-field.ts:73](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L73)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`asyncDebounceMs`](../TanStackField.md#asyncdebouncems)
+
+***
+
+### base
+
+```ts
+base: TanStackFieldInjectable;
+```
+
+Defined in: [app-field.ts:77](https://github.com/TanStack/form/blob/main/packages/angular-form/src/app-field.ts#L77)
+
+***
+
+### cd
+
+```ts
+cd: ChangeDetectorRef;
+```
+
+Defined in: [tanstack-field.ts:238](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L238)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`cd`](../TanStackField.md#cd)
+
+***
+
+### defaultMeta
+
+```ts
+defaultMeta: InputSignal<
+ | undefined
+| Partial>>;
+```
+
+Defined in: [tanstack-field.ts:118](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L118)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`defaultMeta`](../TanStackField.md#defaultmeta)
+
+***
+
+### defaultValue
+
+```ts
+defaultValue: InputSignal>;
+```
+
+Defined in: [tanstack-field.ts:72](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L72)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`defaultValue`](../TanStackField.md#defaultvalue)
+
+***
+
+### disableErrorFlat
+
+```ts
+disableErrorFlat: InputSignal;
+```
+
+Defined in: [tanstack-field.ts:149](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L149)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`disableErrorFlat`](../TanStackField.md#disableerrorflat)
+
+***
+
+### injector
+
+```ts
+injector: Injector;
+```
+
+Defined in: [tanstack-field.ts:222](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L222)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`injector`](../TanStackField.md#injector)
+
+***
+
+### listeners
+
+```ts
+listeners: InputSignal<
+ | undefined
+| NoInfer>>;
+```
+
+Defined in: [tanstack-field.ts:117](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L117)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`listeners`](../TanStackField.md#listeners)
+
+***
+
+### mode
+
+```ts
+mode: InputSignal;
+```
+
+Defined in: [tanstack-field.ts:147](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L147)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`mode`](../TanStackField.md#mode)
+
+***
+
+### name
+
+```ts
+name: InputSignal;
+```
+
+Defined in: [tanstack-field.ts:71](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L71)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`name`](../TanStackField.md#name)
+
+***
+
+### options
+
+```ts
+options: Signal>;
+```
+
+Defined in: [tanstack-field.ts:183](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L183)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`options`](../TanStackField.md#options)
+
+***
+
+### tanstackField
+
+```ts
+tanstackField: InputSignal>;
+```
+
+Defined in: [tanstack-field.ts:79](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L79)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`tanstackField`](../TanStackField.md#tanstackfield)
+
+***
+
+### validators
+
+```ts
+validators: InputSignal<
+ | undefined
+| NoInfer>>;
+```
+
+Defined in: [tanstack-field.ts:97](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L97)
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`validators`](../TanStackField.md#validators)
+
+## Accessors
+
+### api
+
+#### Get Signature
+
+```ts
+get api(): FieldApi
+```
+
+Defined in: [tanstack-field.ts:155](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L155)
+
+##### Returns
+
+`FieldApi`\<`TParentData`, `TName`, `TData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TFormOnMount`, `TFormOnChange`, `TFormOnChangeAsync`, `TFormOnBlur`, `TFormOnBlurAsync`, `TFormOnSubmit`, `TFormOnSubmitAsync`, `TFormOnDynamic`, `TFormOnDynamicAsync`, `TFormOnServer`, `TSubmitMeta`\>
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`api`](../TanStackField.md#api)
+
+## Methods
+
+### ngOnInit()
+
+```ts
+ngOnInit(): void
+```
+
+Defined in: [tanstack-field.ts:240](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L240)
+
+A callback method that is invoked immediately after the
+default change detector has checked the directive's
+data-bound properties for the first time,
+and before any of the view or content children have been checked.
+It is invoked only once when the directive is instantiated.
+
+#### Returns
+
+`void`
+
+#### Inherited from
+
+[`TanStackField`](../tanstackfield.md).[`ngOnInit`](../TanStackField.md#ngoninit)
diff --git a/docs/framework/angular/reference/classes/tanstackfield.md b/docs/framework/angular/reference/classes/tanstackfield.md
index c15d5b235..46d68e3d5 100644
--- a/docs/framework/angular/reference/classes/tanstackfield.md
+++ b/docs/framework/angular/reference/classes/tanstackfield.md
@@ -5,9 +5,13 @@ title: TanStackField
-# Class: TanStackField\
+# Class: TanStackField\
-Defined in: [tanstack-field.directive.ts:31](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L31)
+Defined in: [tanstack-field.ts:37](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L37)
+
+## Extended by
+
+- [`TanStackAppField`](../tanstackappfield.md)
## Type Parameters
@@ -31,6 +35,10 @@ Defined in: [tanstack-field.directive.ts:31](https://github.com/TanStack/form/bl
• **TOnSubmitAsync** *extends* `undefined` \| `FieldAsyncValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+• **TOnDynamic** *extends* `undefined` \| `FieldValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
+• **TOnDynamicAsync** *extends* `undefined` \| `FieldAsyncValidateOrFn`\<`TParentData`, `TName`, `TData`\>
+
• **TFormOnMount** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
• **TFormOnChange** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
@@ -45,6 +53,10 @@ Defined in: [tanstack-field.directive.ts:31](https://github.com/TanStack/form/bl
• **TFormOnSubmitAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
+• **TFormOnDynamic** *extends* `undefined` \| `FormValidateOrFn`\<`TParentData`\>
+
+• **TFormOnDynamicAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
+
• **TFormOnServer** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TParentData`\>
• **TSubmitMeta**
@@ -52,249 +64,184 @@ Defined in: [tanstack-field.directive.ts:31](https://github.com/TanStack/form/bl
## Implements
- `OnInit`
-- `OnChanges`
-- `OnDestroy`
-- `FieldOptions`\<`TParentData`, `TName`, `TData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`\>
## Constructors
### new TanStackField()
```ts
-new TanStackField(): TanStackField
+new TanStackField(): TanStackField
```
+Defined in: [tanstack-field.ts:224](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L224)
+
#### Returns
-[`TanStackField`](tanstackfield.md)\<`TParentData`, `TName`, `TData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TFormOnMount`, `TFormOnChange`, `TFormOnChangeAsync`, `TFormOnBlur`, `TFormOnBlurAsync`, `TFormOnSubmit`, `TFormOnSubmitAsync`, `TFormOnServer`, `TSubmitMeta`\>
+[`TanStackField`](../tanstackfield.md)\<`TParentData`, `TName`, `TData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TFormOnMount`, `TFormOnChange`, `TFormOnChangeAsync`, `TFormOnBlur`, `TFormOnBlurAsync`, `TFormOnSubmit`, `TFormOnSubmitAsync`, `TFormOnDynamic`, `TFormOnDynamicAsync`, `TFormOnServer`, `TSubmitMeta`\>
## Properties
-### api
+### \_api
```ts
-api: FieldApi;
+_api: Signal>;
```
-Defined in: [tanstack-field.directive.ts:129](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L129)
+Defined in: [tanstack-field.ts:151](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L151)
***
-### asyncAlways?
+### asyncAlways
```ts
-optional asyncAlways: boolean;
+asyncAlways: InputSignalWithTransform;
```
-Defined in: [tanstack-field.directive.ts:78](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L78)
-
-If `true`, always run async validation, even if there are errors emitted during synchronous validation.
-
-#### Implementation of
-
-```ts
-FieldOptions.asyncAlways
-```
+Defined in: [tanstack-field.ts:76](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L76)
***
-### asyncDebounceMs?
+### asyncDebounceMs
```ts
-optional asyncDebounceMs: number;
+asyncDebounceMs: InputSignalWithTransform;
```
-Defined in: [tanstack-field.directive.ts:77](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L77)
-
-The default time to debounce async validation if there is not a more specific debounce time passed.
-
-#### Implementation of
-
-```ts
-FieldOptions.asyncDebounceMs
-```
+Defined in: [tanstack-field.ts:73](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L73)
***
-### defaultMeta?
+### cd
```ts
-optional defaultMeta: Partial>;
+cd: ChangeDetectorRef;
```
-Defined in: [tanstack-field.directive.ts:106](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L106)
-
-An optional object with default metadata for the field.
-
-#### Implementation of
-
-```ts
-FieldOptions.defaultMeta
-```
+Defined in: [tanstack-field.ts:238](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L238)
***
-### defaultValue?
+### defaultMeta
```ts
-optional defaultValue: NoInfer;
+defaultMeta: InputSignal<
+ | undefined
+| Partial>>;
```
-Defined in: [tanstack-field.directive.ts:76](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L76)
-
-An optional default value for the field.
-
-#### Implementation of
-
-```ts
-FieldOptions.defaultValue
-```
+Defined in: [tanstack-field.ts:118](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L118)
***
-### disableErrorFlat?
+### defaultValue
```ts
-optional disableErrorFlat: boolean;
+defaultValue: InputSignal>;
```
-Defined in: [tanstack-field.directive.ts:127](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L127)
-
-Disable the `flat(1)` operation on `field.errors`. This is useful if you want to keep the error structure as is. Not suggested for most use-cases.
-
-#### Implementation of
-
-```ts
-FieldOptions.disableErrorFlat
-```
+Defined in: [tanstack-field.ts:72](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L72)
***
-### listeners?
+### disableErrorFlat
```ts
-optional listeners: NoInfer>;
+disableErrorFlat: InputSignal;
```
-Defined in: [tanstack-field.directive.ts:105](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L105)
-
-A list of listeners which attach to the corresponding events
-
-#### Implementation of
-
-```ts
-FieldOptions.listeners
-```
+Defined in: [tanstack-field.ts:149](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L149)
***
-### name
+### injector
```ts
-name: TName;
+injector: Injector;
```
-Defined in: [tanstack-field.directive.ts:75](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L75)
+Defined in: [tanstack-field.ts:222](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L222)
-The field name. The type will be `DeepKeys` to ensure your name is a deep key of the parent dataset.
+***
-#### Implementation of
+### listeners
```ts
-FieldOptions.name
+listeners: InputSignal<
+ | undefined
+| NoInfer>>;
```
+Defined in: [tanstack-field.ts:117](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L117)
+
***
-### tanstackField
+### mode
```ts
-tanstackField: FormApi;
+mode: InputSignal;
```
-Defined in: [tanstack-field.directive.ts:79](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L79)
+Defined in: [tanstack-field.ts:147](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L147)
***
-### unmount()?
+### name
```ts
-optional unmount: () => void;
+name: InputSignal;
```
-Defined in: [tanstack-field.directive.ts:185](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L185)
-
-#### Returns
-
-`void`
+Defined in: [tanstack-field.ts:71](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L71)
***
-### validators?
+### options
```ts
-optional validators: NoInfer>;
+options: Signal>;
```
-Defined in: [tanstack-field.directive.ts:91](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L91)
+Defined in: [tanstack-field.ts:183](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L183)
-A list of validators to pass to the field
+***
-#### Implementation of
+### tanstackField
```ts
-FieldOptions.validators
+tanstackField: InputSignal>;
```
-## Methods
+Defined in: [tanstack-field.ts:79](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L79)
+
+***
-### ngOnChanges()
+### validators
```ts
-ngOnChanges(): void
+validators: InputSignal<
+ | undefined
+| NoInfer>>;
```
-Defined in: [tanstack-field.directive.ts:197](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L197)
+Defined in: [tanstack-field.ts:97](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L97)
-A callback method that is invoked immediately after the
-default change detector has checked data-bound properties
-if at least one has changed, and before the view and content
-children are checked.
+## Accessors
-#### Returns
-
-`void`
-
-#### Implementation of
-
-```ts
-OnChanges.ngOnChanges
-```
-
-***
+### api
-### ngOnDestroy()
+#### Get Signature
```ts
-ngOnDestroy(): void
+get api(): FieldApi
```
-Defined in: [tanstack-field.directive.ts:193](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L193)
+Defined in: [tanstack-field.ts:155](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L155)
-A callback method that performs custom clean-up, invoked immediately
-before a directive, pipe, or service instance is destroyed.
+##### Returns
-#### Returns
-
-`void`
-
-#### Implementation of
+`FieldApi`\<`TParentData`, `TName`, `TData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TFormOnMount`, `TFormOnChange`, `TFormOnChangeAsync`, `TFormOnBlur`, `TFormOnBlurAsync`, `TFormOnSubmit`, `TFormOnSubmitAsync`, `TFormOnDynamic`, `TFormOnDynamicAsync`, `TFormOnServer`, `TSubmitMeta`\>
-```ts
-OnDestroy.ngOnDestroy
-```
-
-***
+## Methods
### ngOnInit()
@@ -302,7 +249,7 @@ OnDestroy.ngOnDestroy
ngOnInit(): void
```
-Defined in: [tanstack-field.directive.ts:187](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L187)
+Defined in: [tanstack-field.ts:240](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.ts#L240)
A callback method that is invoked immediately after the
default change detector has checked the directive's
diff --git a/docs/framework/angular/reference/classes/tanstackfieldinjectable.md b/docs/framework/angular/reference/classes/tanstackfieldinjectable.md
new file mode 100644
index 000000000..ad63cfd31
--- /dev/null
+++ b/docs/framework/angular/reference/classes/tanstackfieldinjectable.md
@@ -0,0 +1,52 @@
+---
+id: TanStackFieldInjectable
+title: TanStackFieldInjectable
+---
+
+
+
+# Class: TanStackFieldInjectable\
+
+Defined in: [injectable.ts:5](https://github.com/TanStack/form/blob/main/packages/angular-form/src/injectable.ts#L5)
+
+## Type Parameters
+
+• **T**
+
+## Constructors
+
+### new TanStackFieldInjectable()
+
+```ts
+new TanStackFieldInjectable(): TanStackFieldInjectable
+```
+
+#### Returns
+
+[`TanStackFieldInjectable`](../tanstackfieldinjectable.md)\<`T`\>
+
+## Properties
+
+### \_api
+
+```ts
+_api: WritableSignal>;
+```
+
+Defined in: [injectable.ts:6](https://github.com/TanStack/form/blob/main/packages/angular-form/src/injectable.ts#L6)
+
+## Accessors
+
+### api
+
+#### Get Signature
+
+```ts
+get api(): FieldApi
+```
+
+Defined in: [injectable.ts:34](https://github.com/TanStack/form/blob/main/packages/angular-form/src/injectable.ts#L34)
+
+##### Returns
+
+`FieldApi`\<`any`, `any`, `T`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`, `any`\>
diff --git a/docs/framework/angular/reference/functions/injectfield.md b/docs/framework/angular/reference/functions/injectfield.md
new file mode 100644
index 000000000..2a744439f
--- /dev/null
+++ b/docs/framework/angular/reference/functions/injectfield.md
@@ -0,0 +1,22 @@
+---
+id: injectField
+title: injectField
+---
+
+
+
+# Function: injectField()
+
+```ts
+function injectField(): TanStackFieldInjectable
+```
+
+Defined in: [injectable.ts:39](https://github.com/TanStack/form/blob/main/packages/angular-form/src/injectable.ts#L39)
+
+## Type Parameters
+
+• **T**
+
+## Returns
+
+[`TanStackFieldInjectable`](../../classes/tanstackfieldinjectable.md)\<`T`\>
diff --git a/docs/framework/angular/reference/functions/injectform.md b/docs/framework/angular/reference/functions/injectform.md
index 08bdeed3c..a65d39b26 100644
--- a/docs/framework/angular/reference/functions/injectform.md
+++ b/docs/framework/angular/reference/functions/injectform.md
@@ -8,7 +8,7 @@ title: injectForm
# Function: injectForm()
```ts
-function injectForm(opts?): FormApi
+function injectForm(opts?): FormApi
```
Defined in: [inject-form.ts:9](https://github.com/TanStack/form/blob/main/packages/angular-form/src/inject-form.ts#L9)
@@ -31,6 +31,10 @@ Defined in: [inject-form.ts:9](https://github.com/TanStack/form/blob/main/packag
• **TOnSubmitAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TFormData`\>
+• **TOnDynamic** *extends* `undefined` \| `FormValidateOrFn`\<`TFormData`\>
+
+• **TOnDynamicAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TFormData`\>
+
• **TOnServer** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TFormData`\>
• **TSubmitMeta**
@@ -39,8 +43,8 @@ Defined in: [inject-form.ts:9](https://github.com/TanStack/form/blob/main/packag
### opts?
-`FormOptions`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnServer`, `TSubmitMeta`\>
+`FormOptions`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TOnServer`, `TSubmitMeta`\>
## Returns
-`FormApi`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnServer`, `TSubmitMeta`\>
+`FormApi`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TOnServer`, `TSubmitMeta`\>
diff --git a/docs/framework/angular/reference/functions/injectstore.md b/docs/framework/angular/reference/functions/injectstore.md
index fad9e10ed..a12d9bfb1 100644
--- a/docs/framework/angular/reference/functions/injectstore.md
+++ b/docs/framework/angular/reference/functions/injectstore.md
@@ -8,7 +8,7 @@ title: injectStore
# Function: injectStore()
```ts
-function injectStore(form, selector?): Signal
+function injectStore(form, selector?): Signal
```
Defined in: [inject-store.ts:9](https://github.com/TanStack/form/blob/main/packages/angular-form/src/inject-store.ts#L9)
@@ -31,17 +31,21 @@ Defined in: [inject-store.ts:9](https://github.com/TanStack/form/blob/main/packa
• **TOnSubmitAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TFormData`\>
+• **TOnDynamic** *extends* `undefined` \| `FormValidateOrFn`\<`TFormData`\>
+
+• **TOnDynamicAsync** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TFormData`\>
+
• **TOnServer** *extends* `undefined` \| `FormAsyncValidateOrFn`\<`TFormData`\>
• **TSubmitMeta**
-• **TSelected** = `NoInfer`\<`FormState`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnServer`\>\>
+• **TSelected** = `NoInfer`\<`FormState`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TOnServer`\>\>
## Parameters
### form
-`FormApi`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnServer`, `TSubmitMeta`\>
+`FormApi`\<`TFormData`, `TOnMount`, `TOnChange`, `TOnChangeAsync`, `TOnBlur`, `TOnBlurAsync`, `TOnSubmit`, `TOnSubmitAsync`, `TOnDynamic`, `TOnDynamicAsync`, `TOnServer`, `TSubmitMeta`\>
### selector?
diff --git a/docs/framework/angular/reference/index.md b/docs/framework/angular/reference/index.md
index 3110ff697..0b69f24c7 100644
--- a/docs/framework/angular/reference/index.md
+++ b/docs/framework/angular/reference/index.md
@@ -9,9 +9,12 @@ title: "@tanstack/angular-form"
## Classes
-- [TanStackField](classes/tanstackfield.md)
+- [TanStackAppField](../classes/tanstackappfield.md)
+- [TanStackField](../classes/tanstackfield.md)
+- [TanStackFieldInjectable](../classes/tanstackfieldinjectable.md)
## Functions
-- [injectForm](functions/injectform.md)
-- [injectStore](functions/injectstore.md)
+- [injectField](../functions/injectfield.md)
+- [injectForm](../functions/injectform.md)
+- [injectStore](../functions/injectstore.md)
diff --git a/docs/framework/lit/guides/basic-concepts.md b/docs/framework/lit/guides/basic-concepts.md
index a67352d3b..efc3a7c60 100644
--- a/docs/framework/lit/guides/basic-concepts.md
+++ b/docs/framework/lit/guides/basic-concepts.md
@@ -86,17 +86,47 @@ For Example:
Each field has its own state, which includes its current value, validation status, error messages, and other metadata. You can access a field's state using its `field.state` property.
-```tsx
+```ts
const {
value,
meta: { errors, isValidating },
} = field.state
```
-There are three field states can be very useful to see how the user interacts with a field. A field is _"touched"_ when the user clicks/tabs into it, _"pristine"_ until the user changes value in it, and _"dirty"_ after the value has been changed. You can check these states via the `isTouched`, `isPristine` and `isDirty` flags, as seen below.
+There are four states in the metadata that can be useful to see how the user interacts with a field:
-```tsx
-const { isTouched, isPristine, isDirty } = field.state.meta
+- _"isTouched"_, after the user changes the field or blurs the field
+- _"isDirty"_, after the field's value has been changed, even if it's been reverted to the default. Opposite of `isPristine`
+- _"isPristine"_, until the user changes the field value. Opposite of `isDirty`
+- _"isBlurred"_, after the field has been blurred
+
+```ts
+const { isTouched, isDirty, isPristine, isBlurred } = field.state.meta
```

+
+## Understanding 'isDirty' in Different Libraries
+
+Non-Persistent `dirty` state
+
+- **Libraries**: React Hook Form (RHF), Formik, Final Form.
+- **Behavior**: A field is 'dirty' if its value differs from the default. Reverting to the default value makes it 'clean' again.
+
+Persistent `dirty` state
+
+- **Libraries**: Angular Form, Vue FormKit.
+- **Behavior**: A field remains 'dirty' once changed, even if reverted to the default value.
+
+We have chosen the persistent 'dirty' state model. To also support a non-persistent 'dirty' state, we introduce an additional flag:
+
+- _"isDefaultValue"_, whether the field's current value is the default value
+
+```ts
+const { isDefaultValue, isTouched } = field.state.meta
+
+// The following line will re-create the non-Persistent `dirty` functionality.
+const nonPersistentIsDirty = !isDefaultValue
+```
+
+
diff --git a/docs/framework/lit/guides/dynamic-validation.md b/docs/framework/lit/guides/dynamic-validation.md
new file mode 100644
index 000000000..c601e4762
--- /dev/null
+++ b/docs/framework/lit/guides/dynamic-validation.md
@@ -0,0 +1,269 @@
+---
+id: dynamic-validation
+title: Dynamic Validation
+---
+
+In many cases, you want to change the validation rules based depending on the state of the form or other conditions. The most popular
+example of this is when you want to validate a field differently based on whether the user has submitted the form for the first time or not.
+
+We support this through our `onDynamic` validation function.
+
+```ts
+import { LitElement, html } from 'lit'
+import { customElement } from 'lit/decorators.js'
+import { TanStackFormController, revalidateLogic } from '@tanstack/lit-form'
+
+@customElement('my-form')
+export class MyForm extends LitElement {
+ #form = new TanStackFormController(this, {
+ defaultValues: {
+ firstName: '',
+ lastName: '',
+ },
+ // If this is omitted, onDynamic will not be called
+ validationLogic: revalidateLogic(),
+ validators: {
+ onDynamic: ({ value }) => {
+ if (!value.firstName) {
+ return { firstName: 'A first name is required' }
+ }
+ return undefined
+ },
+ },
+ })
+
+ render() {
+ return html``
+ }
+}
+```
+
+> By default `onDynamic` is not called, so you need to pass `revalidateLogic()` to the `validationLogic` option of `useForm`.
+
+## Revalidation Options
+
+`revalidateLogic` allows you to specify when validation should be run and change the validation rules dynamically based on the current submission state of the form.
+
+It takes two arguments:
+
+- `mode`: The mode of validation prior to the first form submission. This can be one of the following:
+ - `change`: Validate on every change.
+ - `blur`: Validate on blur.
+ - `submit`: Validate on submit. (**default**)
+
+- `modeAfterSubmission`: The mode of validation after the form has been submitted. This can be one of the following:
+ - `change`: Validate on every change. (**default**)
+ - `blur`: Validate on blur.
+ - `submit`: Validate on submit.
+
+You can, for example, use the following to revalidate on blur after the first submission:
+
+```ts
+@customElement('my-form')
+export class MyForm extends LitElement {
+ #form = new TanStackFormController(this, {
+ // ...
+ validationLogic: revalidateLogic({
+ mode: 'submit',
+ modeAfterSubmission: 'blur',
+ }),
+ // ...
+ })
+}
+```
+
+## Accessing Errors
+
+Just as you might access errors from an `onChange` or `onBlur` validation, you can access the errors from the `onDynamic` validation function using the `form.api.state.errorMap` object.
+
+```ts
+import { LitElement, html } from 'lit'
+import { customElement } from 'lit/decorators.js'
+import { TanStackFormController, revalidateLogic } from '@tanstack/lit-form'
+
+@customElement('my-form')
+export class MyForm extends LitElement {
+ #form = new TanStackFormController(this, {
+ // ...
+ validationLogic: revalidateLogic(),
+ validators: {
+ onDynamic: ({ value }) => {
+ if (!value.firstName) {
+ return { firstName: 'A first name is required' }
+ }
+ return undefined
+ },
+ },
+ })
+
+ render() {
+ return html`