diff --git a/.changeset/rename-onFieldUnmount-to-onUnmount.md b/.changeset/rename-onFieldUnmount-to-onUnmount.md
new file mode 100644
index 000000000..1e7f365af
--- /dev/null
+++ b/.changeset/rename-onFieldUnmount-to-onUnmount.md
@@ -0,0 +1,5 @@
+---
+'@tanstack/form-core': minor
+---
+
+Rename `onFieldUnmount` listener to `onUnmount` to match the naming convention of other form listeners.
diff --git a/docs/framework/angular/guides/listeners.md b/docs/framework/angular/guides/listeners.md
index 729e00a86..0bed8f8e7 100644
--- a/docs/framework/angular/guides/listeners.md
+++ b/docs/framework/angular/guides/listeners.md
@@ -11,7 +11,7 @@ Imagine the following user flow:
- User then selects a province from another drop-down.
- User changes the selected country to a different one.
-In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the onChange event and dispatch a reset to the field "province" when the listener is fired.
+In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the `onChange` event and dispatch a reset to the "province" field when the listener is fired.
Events that can be "listened" to are:
@@ -60,3 +60,79 @@ export class AppComponent {
}
}
```
+
+## Built-in Debouncing
+
+If you are making an API request inside a listener, you may want to debounce the calls as it can lead to performance issues.
+We enable an easy method for debouncing your listeners by adding a `onChangeDebounceMs` or `onBlurDebounceMs`.
+
+```angular-ts
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [TanStackField],
+ template: `
+
+ `,
+})
+export class AppComponent {
+ form = injectForm({
+ defaultValues: {
+ country: '',
+ province: '',
+ },
+ })
+
+ onCountryChange: FieldListenerFn = ({
+ value,
+ }) => {
+ console.log(`Country changed to: ${value} without a change within 500ms, resetting province`)
+ this.form.setFieldValue('province', '')
+ }
+}
+```
+
+## Form listeners
+
+At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange`, `onBlur`, and `onUnmount` propagated to all the form's children. Form-level listeners can also be debounced in the same way as previously discussed.
+
+`onMount` and `onSubmit` listeners have the following parameters:
+
+- `formApi`
+
+`onChange`, `onBlur`, and `onUnmount` listeners have access to:
+
+- `fieldApi`
+- `formApi`
+
+```angular-ts
+export class AppComponent {
+ form = injectForm({
+ listeners: {
+ onMount: ({ formApi }) => {
+ // custom logging service
+ loggingService('mount', formApi.state.values)
+ },
+
+ onChange: ({ formApi, fieldApi }) => {
+ // autosave logic
+ if (formApi.state.isValid) {
+ formApi.handleSubmit()
+ }
+
+ // fieldApi represents the field that triggered the event.
+ console.log(fieldApi.name, fieldApi.state.value)
+ },
+ onChangeDebounceMs: 500,
+ },
+ })
+}
+```
diff --git a/docs/framework/lit/guides/listeners.md b/docs/framework/lit/guides/listeners.md
new file mode 100644
index 000000000..6f72f801f
--- /dev/null
+++ b/docs/framework/lit/guides/listeners.md
@@ -0,0 +1,126 @@
+---
+id: listeners
+title: Side effects for event triggers
+---
+
+For situations where you want to "affect" or "react" to triggers, there's the listener API. For example, if you, as the developer, want to reset a form field as a result of another field changing, you would use the listener API.
+
+Imagine the following user flow:
+
+- User selects a country from a drop-down.
+- User then selects a province from another drop-down.
+- User changes the selected country to a different one.
+
+In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the `onChange` event and dispatch a reset to the "province" field when the listener is fired.
+
+Events that can be "listened" to are:
+
+- `onChange`
+- `onBlur`
+- `onMount`
+- `onSubmit`
+- `onUnmount`
+
+```ts
+${this.#form.field(
+ {
+ name: 'country',
+ listeners: {
+ onChange: ({ value }) => {
+ console.log(`Country changed to: ${value}, resetting province`)
+ this.#form.api.setFieldValue('province', '')
+ },
+ },
+ },
+ (field) => {
+ return html`
+
+ `
+ },
+)}
+
+${this.#form.field(
+ { name: 'province' },
+ (field) => {
+ return html`
+
+ `
+ },
+)}
+```
+
+## Built-in Debouncing
+
+If you are making an API request inside a listener, you may want to debounce the calls as it can lead to performance issues.
+We enable an easy method for debouncing your listeners by adding a `onChangeDebounceMs` or `onBlurDebounceMs`.
+
+```ts
+${this.#form.field(
+ {
+ name: 'country',
+ listeners: {
+ onChangeDebounceMs: 500, // 500ms debounce
+ onChange: ({ value }) => {
+ console.log(`Country changed to: ${value} without a change within 500ms, resetting province`)
+ this.#form.api.setFieldValue('province', '')
+ },
+ },
+ },
+ (field) => {
+ return html``
+ },
+)}
+```
+
+## Form listeners
+
+At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange`, `onBlur`, and `onUnmount` propagated to all the form's children. Form-level listeners can also be debounced in the same way as previously discussed.
+
+`onMount` and `onSubmit` listeners have the following parameters:
+
+- `formApi`
+
+`onChange`, `onBlur`, and `onUnmount` listeners have access to:
+
+- `fieldApi`
+- `formApi`
+
+```ts
+#form = new TanStackFormController(this, {
+ listeners: {
+ onMount: ({ formApi }) => {
+ // custom logging service
+ loggingService('mount', formApi.state.values)
+ },
+
+ onChange: ({ formApi, fieldApi }) => {
+ // autosave logic
+ if (formApi.state.isValid) {
+ formApi.handleSubmit()
+ }
+
+ // fieldApi represents the field that triggered the event.
+ console.log(fieldApi.name, fieldApi.state.value)
+ },
+ onChangeDebounceMs: 500,
+ },
+})
+```
diff --git a/docs/framework/react/guides/listeners.md b/docs/framework/react/guides/listeners.md
index f42cb6dcd..179d87cd2 100644
--- a/docs/framework/react/guides/listeners.md
+++ b/docs/framework/react/guides/listeners.md
@@ -93,13 +93,13 @@ We enable an easy method for debouncing your listeners by adding a `onChangeDebo
### Form listeners
-At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange` and `onBlur` propagated to all the form's children. Form-level listeners can also be debounced in the same way as previously discussed.
+At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange`, `onBlur`, and `onUnmount` propagated to all the form's children. Form-level listeners can also be debounced in the same way as previously discussed.
`onMount` and `onSubmit` listeners have the following parameters:
- `formApi`
-`onChange` and `onBlur` listeners have access to:
+`onChange`, `onBlur`, and `onUnmount` listeners have access to:
- `fieldApi`
- `formApi`
diff --git a/docs/framework/solid/guides/listeners.md b/docs/framework/solid/guides/listeners.md
new file mode 100644
index 000000000..c33ee6345
--- /dev/null
+++ b/docs/framework/solid/guides/listeners.md
@@ -0,0 +1,127 @@
+---
+id: listeners
+title: Side effects for event triggers
+---
+
+For situations where you want to "affect" or "react" to triggers, there's the listener API. For example, if you, as the developer, want to reset a form field as a result of another field changing, you would use the listener API.
+
+Imagine the following user flow:
+
+- User selects a country from a drop-down.
+- User then selects a province from another drop-down.
+- User changes the selected country to a different one.
+
+In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the `onChange` event and dispatch a reset to the "province" field when the listener is fired.
+
+Events that can be "listened" to are:
+
+- `onChange`
+- `onBlur`
+- `onMount`
+- `onSubmit`
+- `onUnmount`
+
+```tsx
+export default function App() {
+ const form = createForm(() => ({
+ defaultValues: {
+ country: '',
+ province: '',
+ },
+ // ...
+ }))
+
+ return (
+
+ )
+}
+```
+
+## Built-in Debouncing
+
+If you are making an API request inside a listener, you may want to debounce the calls as it can lead to performance issues.
+We enable an easy method for debouncing your listeners by adding a `onChangeDebounceMs` or `onBlurDebounceMs`.
+
+```tsx
+ {
+ console.log(`Country changed to: ${value} without a change within 500ms, resetting province`)
+ form.setFieldValue('province', '')
+ },
+ }}
+>
+ {(field) => (
+ /* ... */
+ )}
+
+```
+
+## Form listeners
+
+At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange`, `onBlur`, and `onUnmount` propagated to all the form's children. Form-level listeners can also be debounced in the same way as previously discussed.
+
+`onMount` and `onSubmit` listeners have the following parameters:
+
+- `formApi`
+
+`onChange`, `onBlur`, and `onUnmount` listeners have access to:
+
+- `fieldApi`
+- `formApi`
+
+```tsx
+const form = createForm(() => ({
+ listeners: {
+ onMount: ({ formApi }) => {
+ // custom logging service
+ loggingService('mount', formApi.state.values)
+ },
+
+ onChange: ({ formApi, fieldApi }) => {
+ // autosave logic
+ if (formApi.state.isValid) {
+ formApi.handleSubmit()
+ }
+
+ // fieldApi represents the field that triggered the event.
+ console.log(fieldApi.name, fieldApi.state.value)
+ },
+ onChangeDebounceMs: 500,
+ },
+}))
+```
diff --git a/docs/framework/svelte/guides/listeners.md b/docs/framework/svelte/guides/listeners.md
new file mode 100644
index 000000000..d7a4a5453
--- /dev/null
+++ b/docs/framework/svelte/guides/listeners.md
@@ -0,0 +1,131 @@
+---
+id: listeners
+title: Side effects for event triggers
+---
+
+For situations where you want to "affect" or "react" to triggers, there's the listener API. For example, if you, as the developer, want to reset a form field as a result of another field changing, you would use the listener API.
+
+Imagine the following user flow:
+
+- User selects a country from a drop-down.
+- User then selects a province from another drop-down.
+- User changes the selected country to a different one.
+
+In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the `onChange` event and dispatch a reset to the "province" field when the listener is fired.
+
+Events that can be "listened" to are:
+
+- `onChange`
+- `onBlur`
+- `onMount`
+- `onSubmit`
+- `onUnmount`
+
+```svelte
+
+
+
+```
+
+## Built-in Debouncing
+
+If you are making an API request inside a listener, you may want to debounce the calls as it can lead to performance issues.
+We enable an easy method for debouncing your listeners by adding a `onChangeDebounceMs` or `onBlurDebounceMs`.
+
+```svelte
+ {
+ console.log(`Country changed to: ${value} without a change within 500ms, resetting province`)
+ form.setFieldValue('province', '')
+ },
+ }}
+>
+ {#snippet children(field)}
+
+ {/snippet}
+
+```
+
+## Form listeners
+
+At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange`, `onBlur`, and `onUnmount` propagated to all the form's children. Form-level listeners can also be debounced in the same way as previously discussed.
+
+`onMount` and `onSubmit` listeners have the following parameters:
+
+- `formApi`
+
+`onChange`, `onBlur`, and `onUnmount` listeners have access to:
+
+- `fieldApi`
+- `formApi`
+
+```svelte
+
+```
diff --git a/docs/framework/vue/guides/listeners.md b/docs/framework/vue/guides/listeners.md
index 946af1e7f..b8b8171a7 100644
--- a/docs/framework/vue/guides/listeners.md
+++ b/docs/framework/vue/guides/listeners.md
@@ -11,7 +11,7 @@ Imagine the following user flow:
- User then selects a province from another drop-down.
- User changes the selected country to a different one.
-In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the onChange event and dispatch a reset to the field "province" when the listener is fired.
+In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the `onChange` event and dispatch a reset to the "province" field when the listener is fired.
Events that can be "listened" to are:
@@ -64,3 +64,66 @@ const form = useForm({
```
+
+## Built-in Debouncing
+
+If you are making an API request inside a listener, you may want to debounce the calls as it can lead to performance issues.
+We enable an easy method for debouncing your listeners by adding a `onChangeDebounceMs` or `onBlurDebounceMs`.
+
+```vue
+
+
+
+
+
+```
+
+## Form listeners
+
+At a higher level, listeners are also available at the form level, allowing you access to the `onMount` and `onSubmit` events, and having `onChange`, `onBlur`, and `onUnmount` propagated to all the form's children. Form-level listeners can also be debounced in the same way as previously discussed.
+
+`onMount` and `onSubmit` listeners have the following parameters:
+
+- `formApi`
+
+`onChange`, `onBlur`, and `onUnmount` listeners have access to:
+
+- `fieldApi`
+- `formApi`
+
+```vue
+
+```
diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts
index a064a83de..6e43c7b5d 100644
--- a/packages/form-core/src/FieldApi.ts
+++ b/packages/form-core/src/FieldApi.ts
@@ -1399,7 +1399,7 @@ export class FieldApi<
fieldApi: this,
})
- this.form.options.listeners?.onFieldUnmount?.({
+ this.form.options.listeners?.onUnmount?.({
formApi: this.form,
fieldApi: this,
})
diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts
index 748fca0fe..350b03657 100644
--- a/packages/form-core/src/FormApi.ts
+++ b/packages/form-core/src/FormApi.ts
@@ -307,7 +307,7 @@ export interface FormListeners<
meta: TSubmitMeta
}) => void
- onFieldUnmount?: (props: {
+ onUnmount?: (props: {
formApi: FormApi<
TFormData,
TOnMount,
diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts
index 1fa3d4d65..8c808903e 100644
--- a/packages/form-core/tests/FieldApi.spec.ts
+++ b/packages/form-core/tests/FieldApi.spec.ts
@@ -1514,7 +1514,7 @@ describe('field api', () => {
expect(triggered).toStrictEqual('test')
})
- it('should run form listener onFieldUnmount', () => {
+ it('should run form listener onUnmount', () => {
let capturedName: string | undefined
const form = new FormApi({
@@ -1522,7 +1522,7 @@ describe('field api', () => {
name: 'test',
},
listeners: {
- onFieldUnmount: ({ fieldApi }) => {
+ onUnmount: ({ fieldApi }) => {
capturedName = fieldApi.name as string
},
},
diff --git a/packages/react-form/tests/useField.test.tsx b/packages/react-form/tests/useField.test.tsx
index bcf7ed25c..c2afa62c9 100644
--- a/packages/react-form/tests/useField.test.tsx
+++ b/packages/react-form/tests/useField.test.tsx
@@ -440,7 +440,7 @@ describe('useField', () => {
name: 'test',
},
listeners: {
- onFieldUnmount: formFieldUnmount,
+ onUnmount: formFieldUnmount,
},
})