# Event Handlers
*Handle user interactions and input events in Ivy with event handlers like OnBlur, enabling [validation](./13_Forms.md), [data persistence](./19_Clients.md), and reactive user experiences.*
Event handlers allow you to respond to user interactions with [widgets](./03_Widgets.md) in your Ivy [applications](./15_Apps.md). They enable you to execute custom logic when users interact with UI elements, such as clicking buttons, changing input values, or moving focus between fields.
## Basic Usage
The simplest form of `OnBlur` handler performs an action when the input loses focus:
```csharp
public class BasicBlurExample : ViewBase
{
public override object? Build()
{
var name = UseState("");
var message = UseState("");
return Layout.Vertical()
| name.ToTextInput("Your Name")
.Placeholder("Enter your name...")
.HandleBlur(_ => message.Set($"Hello, {name.Value}!"))
| Text.P(message.Value);
}
}
```
## OnBlur Event Handler
The `OnBlur` event handler is triggered when an input widget loses focus. This is particularly useful for validation, auto-saving data, analytics tracking, or performing cleanup operations when a user finishes interacting with a field.
### When OnBlur Fires
```mermaid
graph LR
A[User types in field] --> B[User clicks away]
B --> C[OnBlur fires]
```
### Available on Input Widgets
The `OnBlur` event handler is available on all input widgets that implement the `IAnyInput` interface:
| Input Widget | Description |
|--------------|-------------|
| [TextInput](../../02_Widgets/04_Inputs/02_TextInput.md) | Text, password, email, search, and textarea inputs |
| [NumberInput](../../02_Widgets/04_Inputs/03_NumberInput.md) | Number and slider inputs |
| [SelectInput](../../02_Widgets/04_Inputs/05_SelectInput.md) | Dropdown select inputs |
| [AsyncSelectInput](../../02_Widgets/04_Inputs/06_AsyncSelectInput.md) | Async dropdown inputs with server-side data |
| [BoolInput](../../02_Widgets/04_Inputs/04_BoolInput.md) | Checkbox and switch inputs |
| [DateTimeInput](../../02_Widgets/04_Inputs/07_DateTimeInput.md) | Date and time picker inputs |
| [DateRangeInput](../../02_Widgets/04_Inputs/08_DateRangeInput.md) | Date range picker inputs |
| [FileInput](../../02_Widgets/04_Inputs/10_FileInput.md) | File upload inputs |
| [ColorInput](../../02_Widgets/04_Inputs/09_ColorInput.md) | Color picker inputs |
| [CodeInput](../../02_Widgets/04_Inputs/11_CodeInput.md) | Code editor inputs |
| [FeedbackInput](../../02_Widgets/04_Inputs/13_FeedbackInput.md) | Star rating and feedback inputs |
| [ReadOnlyInput](../../02_Widgets/04_Inputs/14_ReadOnlyInput.md) | Read-only display inputs |
## Common Patterns
### Validation Pattern
Validate [fields](../../02_Widgets/04_Inputs/01_Field.md) when the user finishes editing using validation patterns:
```csharp
public class ValidationBlurExample : ViewBase
{
public override object? Build()
{
var email = UseState("");
var error = UseState(() => (string?)null);
return email.ToTextInput()
.Placeholder("your.email@example.com")
.HandleBlur(() =>
{
error.Set(string.IsNullOrWhiteSpace(email.Value) ? "Required"
: !email.Value.Contains("@") ? "Invalid email"
: null);
})
.Invalid(error.Value);
}
}
```
### Auto-Save & Formatting Pattern
Perform actions like saving or formatting when focus is lost:
```csharp
public class AutoSaveFormatExample : ViewBase
{
public override object? Build()
{
var phoneNumber = UseState("");
var title = UseState("");
var lastSaved = UseState(() => (DateTime?)null);
var client = UseService<IClientProvider>();
return Layout.Vertical()
// Auto-save pattern
| title.ToTextInput()
.Placeholder("Document title")
.HandleBlur(async () =>
{
await Task.Delay(500); // Save to database
lastSaved.Set(DateTime.Now);
client.Toast("Saved!");
})
| Text.Muted(lastSaved.Value != null ? $"Saved at {lastSaved.Value:HH:mm:ss}" : "")
// Format pattern
| phoneNumber.ToTextInput()
.Placeholder("Enter 10-digit phone")
.HandleBlur(() =>
{
var digits = new string(phoneNumber.Value.Where(char.IsDigit).ToArray());
if (digits.Length == 10)
phoneNumber.Set($"({digits.Substring(0, 3)}) {digits.Substring(3, 3)}-{digits.Substring(6, 4)}");
});
}
}
```
### Async Operations Pattern
Handle async operations like API validation:
```csharp
public class AsyncBlurExample : ViewBase
{
public override object? Build()
{
var username = UseState("");
var message = UseState("");
return Layout.Vertical()
| username.ToTextInput()
.Placeholder("Choose username")
.HandleBlur(async () =>
{
if (string.IsNullOrWhiteSpace(username.Value)) return;
message.Set("Checking...");
await Task.Delay(1000); // API call
var isAvailable = !username.Value.Equals("admin", StringComparison.OrdinalIgnoreCase);
message.Set(isAvailable ? "Available" : "Taken");
})
| Text.P(message.Value);
}
}
```
## HandleBlur Method Signatures
```csharp
// Simple action (most common)
input.HandleBlur(() => Validate());
// With event parameter
input.HandleBlur((Event<IAnyInput> e) => Log(e.Id));
// Async operation
input.HandleBlur(async () => await SaveToApi());
```
## See Also
- [Forms](./13_Forms.md) - Building forms with validation
- [State](../../03_Hooks/02_Core/03_UseState.md) - Managing component state
- [Effects](../../03_Hooks/02_Core/04_UseEffect.md) - Performing side effects
- [Widgets](./03_Widgets.md) - Understanding Ivy widgets