# Views
Understand how Views work as the core building blocks of Ivy [apps](./10_Apps.md), similar to React components but written entirely in C#.
Views are the fundamental building blocks of Ivy apps. They are similar to React components, providing a way to encapsulate UI logic and [state management](../../03_Hooks/02_Core/03_UseState.md) in a reusable way. Every view inherits from `ViewBase` and implements a `Build()` method that returns the UI structure.
## Basic Usage
Here's a simple view that displays a greeting:
```csharp
Text.P("Hello, World!")
```
### The ViewBase Class
All views inherit from the abstract `ViewBase` class, which provides:
- **Build() method**: The core method that returns the UI structure
- **Lifecycle management**: Automatic disposal and cleanup
- **Hook access**: Built-in state management and effect [hooks](../../03_Hooks/02_RulesOfHooks.md)
- **Service injection**: Access to [application services](../../03_Hooks/02_Core/11_UseService.md)
- **Context management**: Shared data between parent and child views
### Build Method
The `Build()` method is the heart of every view. It can return:
- [Widgets](./03_Widgets.md) ([Button](../../02_Widgets/03_Common/01_Button.md), [Card](../../02_Widgets/03_Common/04_Card.md), Text, etc.)
- Other Views (for composition)
- [Layouts](./04_Layout.md) (to arrange multiple elements)
- Primitive types (strings, numbers)
- Collections (arrays, lists)
- `null` (to render nothing)
```csharp
public class FlexibleContentView : ViewBase
{
public override object? Build()
{
var showContent = UseState(true);
return Layout.Vertical()
| new Button($"{(showContent.Value ? "Hide" : "Show")} Content",
onClick: _ => showContent.Set(!showContent.Value))
| (showContent.Value ? "This content can be toggled!" : null);
}
}
```
### State Management with Hooks
Views use React-like hooks for state management. The most common hook is [UseState()](../../03_Hooks/02_Core/03_UseState.md):
```csharp
public class CounterView : ViewBase
{
public override object? Build()
{
var count = UseState(0);
return new Card(
Layout.Vertical().Align(Align.Center).Gap(4)
| Text.P($"{count.Value}")
| (Layout.Horizontal().Gap(2).Align(Align.Center)
| new Button("-", onClick: _ => count.Set(count.Value - 1))
| new Button("Reset", onClick: _ => count.Set(0))
| new Button("+", onClick: _ => count.Set(count.Value + 1)))
).Title("Counter");
}
}
```
### Rules of Hooks
To ensure correct state management, Ivy hooks must follow specific rules.
Read the full guide on **[Rules of Hooks](../../03_Hooks/02_RulesOfHooks.md)** to learn more and troubleshoot common errors.
### State Initialization
You can initialize state in multiple ways:
```csharp
// Direct value
var count = UseState(0);
// Lazy initialization (called only once)
var expensiveData = UseState(() => ComputeExpensiveData());
// State that doesn't trigger rebuilds
var cache = UseState(new Dictionary<string, object>(), buildOnChange: false);
```
## Service Injection
Views can access application services using the [UseService<T>()](../../03_Hooks/02_Core/11_UseService.md) hook:
```csharp demo
new Button("Show Toast",
onClick: _ => client.Toast("Hello from service!", "Service Demo"))
```
## Effects and Side Effects
Use `UseEffect()` for [side effects](../../03_Hooks/02_Core/04_UseEffect.md) like API calls, timers, or [subscriptions](../../03_Hooks/02_Core/10_UseSignal.md):
```csharp
public class TimerView : ViewBase
{
public override object? Build()
{
var time = UseState(DateTime.Now);
// Update time every second
UseEffect(async () =>
{
while (true)
{
await Task.Delay(1000);
time.Set(DateTime.Now);
}
});
return Text.P($"Current time: {time.Value:HH:mm:ss}");
}
}
```
### View Composition
Views can be composed together to create complex UIs:
```csharp
Layout.Vertical()
| Text.H2("Team Members")
| new Card(
Layout.Vertical()
| Text.H4("Alice Smith")
| Text.P("alice@example.com").Small().Color(Colors.Gray)
| new Badge("Admin").Secondary()
)
| new Card(
Layout.Vertical()
| Text.H4("Bob Johnson")
| Text.P("bob@example.com").Small().Color(Colors.Gray)
| new Badge("User").Secondary()
)
| new Card(
Layout.Vertical()
| Text.H4("Carol Brown")
| Text.P("carol@example.com").Small().Color(Colors.Gray)
| new Badge("Manager").Secondary()
)
```
### App Attribute
To make a view available as an app, use the `[App]` attribute:
```csharp
[App(icon: Icons.Home, title: "My App")]
public class MyApp : ViewBase
{
public override object? Build()
{
return Text.H1("Welcome to My App!");
}
}
```
The `[App]` attribute supports several properties:
- `icon`: [Icon](../../02_Widgets/01_Primitives/02_Icon.md) to display in [navigation](./09_Navigation.md)
- `title`: Display name (defaults to class name)
- `path`: [Navigation](./09_Navigation.md) path array for hierarchical organization
- `isVisible`: Whether to show in navigation
- `searchHints`: Alternative keywords for search discoverability
- `order`: Sort order within group
- `description`: Brief description of the app
For enhanced search discoverability, use `searchHints` to provide alternative keywords:
```csharp
[App(icon: Icons.TextCursorInput,
path: ["Widgets", "Inputs"],
searchHints: ["password", "textarea", "search", "email"])]
public class TextInputApp : ViewBase
{
public override object? Build()
{
return Text.H1("Text Input Examples");
}
}
```
## Advanced Patterns
### Conditional Rendering
```csharp
public class ConditionalView : ViewBase
{
public override object? Build()
{
var isLoggedIn = UseState(false);
return Layout.Vertical()
| new Button(isLoggedIn.Value ? "Logout" : "Login",
onClick: _ => isLoggedIn.Set(!isLoggedIn.Value))
| (isLoggedIn.Value
? Text.Success("Welcome back!")
: Text.Muted("Please log in to continue"));
}
}
```
### Dynamic Lists
```csharp
public class TodoApp : ViewBase
{
public override object? Build()
{
var todos = UseState(new List<string>());
var newTodo = UseState("");
return Layout.Vertical()
| new Card(
Layout.Vertical()
| (Layout.Horizontal()
| newTodo.ToTextInput(placeholder: "Add a todo...").Width(Size.Grow())
| new Button("Add", onClick: _ => {
if (!string.IsNullOrWhiteSpace(newTodo.Value))
{
todos.Set([..todos.Value, newTodo.Value]);
newTodo.Set("");
}
}).Icon(Icons.Plus))
| todos.Value.Select((todo, index) =>
Layout.Horizontal()
| Text.Literal(todo).Width(Size.Grow())
| new Button("Remove", onClick: _ => {
var list = todos.Value.ToList();
list.RemoveAt(index);
todos.Set(list);
}).Icon(Icons.Trash).Variant(ButtonVariant.Outline).Small()
)
).Title("Todo List");
}
}
```
### Simple User Profile Example
```csharp
new Card(
Layout.Vertical()
| new Avatar("John Doe", "JD")
| (Layout.Horizontal()
| Text.P("John Doe")
| Text.P("42 posts"))
| new Button("Follow")
.Variant(ButtonVariant.Primary)
.Width(Size.Full())
)
```