diff --git a/adev/src/content/guide/forms/signals/custom-controls.md b/adev/src/content/guide/forms/signals/custom-controls.md
index 0dddc428f4b2..f561ce566754 100644
--- a/adev/src/content/guide/forms/signals/custom-controls.md
+++ b/adev/src/content/guide/forms/signals/custom-controls.md
@@ -340,6 +340,54 @@ When the user types an invalid email, the FormField directive automatically upda
Most state properties use `input()` (read-only from the form). Use `model()` for `touched` when your control updates it on user interaction. The `touched` property uniquely supports `model()`, `input()`, or `OutputRef` depending on your needs.
+### Working with `debounce('blur')`
+
+The [`debounce('blur')`](api/forms/signals/debounce) rule delays updates from the UI to the form model until the field is blurred, instead of applying them on every keystroke. Built-in controls report a blur to the form automatically. A custom control only participates if it emits its `touch` output in response to the native `blur` event:
+
+```angular-ts
+import {Component, model, output} from '@angular/core';
+import {FormValueControl} from '@angular/forms/signals';
+
+@Component({
+ selector: 'app-custom-input',
+ template: `
+
+ `,
+})
+export class CustomInput implements FormValueControl {
+ value = model('');
+ touch = output();
+}
+```
+
+With the `touch` output in place, `debounce('blur')` behaves the same for your control as it does for built-in inputs:
+
+```angular-ts
+import {Component, signal} from '@angular/core';
+import {debounce, form, FormField} from '@angular/forms/signals';
+import {CustomInput} from './custom-input';
+
+@Component({
+ selector: 'app-root',
+ imports: [CustomInput, FormField],
+ template: ``,
+})
+export class App {
+ userModel = signal({name: ''});
+
+ userForm = form(this.userModel, (schemaPath) => {
+ debounce(schemaPath.name, 'blur');
+ });
+}
+```
+
+IMPORTANT: Emit `touch` on `blur` (when focus leaves the control), not on `focus`. Without the `touch` output the field never registers as blurred, so `debounce('blur')` has no effect on your control.
+
## Value transformation
Controls sometimes display values differently than the form model stores them - a date picker might display "January 15, 2024" while storing "2024-01-15", or a currency input might show "$1,234.56" while storing 1234.56.
diff --git a/packages/forms/signals/src/api/control.ts b/packages/forms/signals/src/api/control.ts
index b5c66447c163..8fe73db8c7b2 100644
--- a/packages/forms/signals/src/api/control.ts
+++ b/packages/forms/signals/src/api/control.ts
@@ -113,7 +113,10 @@ export interface FormUiControl {
| InputSignal
| InputSignalWithTransform;
/**
- * An output to emit when the control is touched.
+ * An output to emit when the user finishes interacting with the control, marking the field as
+ * touched. Emit this in response to the native `blur` event (when focus leaves the control), not
+ * `focus`. The `Field` directive listens to this output to update the field's touched status,
+ * which blur-based rules such as `debounce('blur')` rely on.
*/
readonly touch?: OutputRef;
/**
diff --git a/packages/forms/signals/src/api/rules/debounce.ts b/packages/forms/signals/src/api/rules/debounce.ts
index fb01bb93e108..5bde1515562f 100644
--- a/packages/forms/signals/src/api/rules/debounce.ts
+++ b/packages/forms/signals/src/api/rules/debounce.ts
@@ -21,6 +21,9 @@ import type {Debouncer, PathKind, SchemaPath, SchemaPathRules} from '../types';
* @param config A debounce configuration, which can be either a debounce duration in milliseconds,
* `'blur'` to debounce until the field is blurred, or a custom {@link Debouncer} function.
*
+ * @see [Custom form controls](guide/forms/signals/custom-controls) for using `debounce('blur')` with
+ * a custom `FormValueControl`.
+ *
* @publicApi 22.0
*/
export function debounce(