Ivy for Blazor Developers

A practical guide for Blazor developers exploring Ivy. Covers how Blazor concepts - Components, Parameters, State management, and Razor syntax map to Ivy's C# equivalents.

If you know Blazor, you'll feel right at home with Ivy. You are already used to writing C# on the server (Blazor Server) or client. The main shift is moving from the HTML-mixed Razor syntax to a pure, fluent C# API. No more context switching between markup and logic it's all just C#.

The Familiar Patterns

Components Are Classes

In Blazor, you typically build with .razor files which compile down to classes. In Ivy, you write the C# classes directly by inheriting from ViewBase.

Blazor:

<div class="card">
    <h2>@Name</h2>
    <p>@Email</p>
</div>

@code {
    [Parameter]
    public required string Name { get; set; }
    
    [Parameter]
    public required string Email { get; set; }
}

Ivy:

public class UserCard : ViewBase
{
    public required string Name { get; init; }
    public required string Email { get; init; }

    public override object? Build()
    {
        return new Card()
            | Text.H2(Name)
            | Text.P(Email);
    }
}

Same concept. Instead of [Parameter] properties and a razor template, you use standard properties and a Build() method that returns the widget tree.

State Management

Blazor developers are used to modifying fields and calling StateHasChanged() (or relying on the event handler to do it). Ivy uses a hook-like pattern similar to UseState , which makes reactivity more explicit and fine-grained.

Blazor

<p>Count: @currentCount</p>
<button @onclick="IncrementCount">Count</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        // StateHasChanged() is called automatically for EventCallbacks
    }
}

Ivy:

public class Counter : ViewBase
{
    public override object? Build()
    {
        // define reactive state
        var count = UseState(0);

        return new Button($"Count: {count.Value}",
            onClick: _ => count.Set(count.Value + 1));
    }
}

The mental model is similar: data changes update the UI. However, Ivy's UseState eliminates the ambiguity of when to call StateHasChanged() setting the value automatically triggers a targeted re-render.

What's Different (and Better)

No Razor Syntax

Blazor requires you to mix HTML and C#:

@if (isVisible) {
    <div class="panel">...</div>
}

Ivy allows you to use standard C# control flow directly in your component construction:

if (isVisible.Value) 
{
    return new Panel()...;
}

You don't need to learn a templating language. If you know C#, you know how to build Ivy UIs.

Lifecycle vs. Hooks

Blazor relies on lifecycle methods like OnInitializedAsync and OnParametersSet . Ivy uses the UseEffect pattern, which creates side effects based on dependency changes.

Blazor:

protected override async Task OnInitializedAsync()
{
    users = await UserService.GetUsers();
}

Ivy

UseEffect(async () => {
    var data = await userService.GetUsers();
    users.Set(data);
}, []); // Empty array means "run once on mount"

Server-Side Simplicity

Like Blazor Server, Ivy runs on the server. However, Ivy sends efficiently diffed JSON patches to render native HTML widgets on the client, rather than managing a virtual DOM diff over SignalR in the same way. The browser only ever sees the rendered result, keeping your logic secure.

Layout Composition

Blazor uses RenderFragment and <ChildContent> for composition. Ivy uses the pipe operator | for a more fluent, functional composition style.

Blazor

<StackLayout Gap="4">
    <Header />
    <Sidebar />
    <Content />
</StackLayout>

Ivy:

Layout.Vertical().Gap(4)
    | new Header()
    | new Sidebar()
    | new Content()

The pipe operator (|) is syntactic sugar for adding children. It's composable, readable, and feels natural once you use it.

##Dependency Injection Blazor uses the @inject directive. Ivy uses UseService , leveraging the same underlying ASP.NET Core DI container you already know.

Blazor

@inject IWeatherService WeatherService

@code {
    // use WeatherService
}

Ivy:

var weatherService = UseService<IWeatherService>();

Async Data Loading

Handling async data in Blazor often involves checking for null or loading flags in the markup. Ivy handles this with standard C# flow.

Blazor

@if (forecasts == null)
{
    <p>Loading...</p>
}
else
{
    <Grid Items="forecasts" />
}

Ivy

public override object? Build()
{
    var users = UseState<List<User>>([]);
    var loading = UseState(true);
    var userService = UseService<UserService>();

    UseEffect(async () => {
        var data = await userService.GetUsersAsync();
        users.Set(data);
        loading.Set(false);
    }, []);

    if (loading.Value) return new Skeleton();

    return users.Value.ToTable();
}

Getting Started

If you're a Blazor developer ready to try Ivy:

First, make sure you have installed the .NET 9 SDK.

dotnet tool install -g Ivy.Console
ivy init --hello
dotnet watch

You'll have a running app with hot reload in seconds. Open the code, and you'll feel right at home.

The Bottom Line

Ivy isn't asking you to learn a new language. It's asking you to use the C# you already love for the entire stack including the UI structure. If you've ever felt that context-switching between Razor syntax and C# logic was friction, Ivy removes that barrier.

Your backend logic stays the same. Your DI stays the same. You just write pure C# Views instead of Razor files.

Resources:

Diana Horbyk

Written by

Diana Horbyk