Cover Image for Ivy for React Developers

Ivy for React Developers

A practical guide for React developers exploring Ivy. Covers how React concepts - useState, useEffect, component composition, and event handling - map directly to Ivy's C# equivalents.

If you know React, you already know 80% of Ivy. The mental model is identical - components, hooks, declarative UI - just expressed in C# instead of JavaScript. No JSX, no bundlers, no node_modules. Just your existing React intuition applied to a full-stack C# framework.

The Familiar Patterns

Components Are Views

In React, you build with functional components. In Ivy, you build with Views:

React:

function UserCard({ name, email }) {
  return (
    <div className="card">
      <h2>{name}</h2>
      <p>{email}</p>
    </div>
  );
}

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, different syntax. The Build() method is your render function.

Hooks Work the Same Way

This is where Ivy really shines for React developers. The hooks you know and love have direct equivalents:

React Ivy Purpose
useState UseState Local component state
useEffect UseEffect Side effects with dependencies
useContext UseService or UseContext Dependency injection
useRef UseRef Mutable references that don't trigger rebuild

React:

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

Ivy:

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

        UseEffect(() => {
            Console.WriteLine($"Count: {count.Value}");
        }, [count]);

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

The mental model is identical. State changes trigger re-renders. Effects run when dependencies change. You already understand this.

What's Different (and Better)

No Build Pipeline

React requires: npm, webpack/vite, babel, node_modules, package.json, build scripts...

Ivy requires:

dotnet watch

That's it. Hot reload included. No configuration needed.

No API Layer Needed

In React, you typically:

  1. Build your UI
  2. Create API endpoints
  3. Fetch data with useEffect + fetch/axios
  4. Handle loading/error states
  5. Sync frontend and backend types

In Ivy, you just... use your data:

public override object? Build()
{
    var db = UseService<MyDbContext>();
    var users = db.Users.Where(u => u.IsActive).ToList();

    return users.ToTable();
}

Your C# code runs on the server. Direct database access. No REST endpoints to maintain. No type mismatches between frontend and backend.

Server-Side State = Security

In React, state lives in the browser. Sensitive logic requires careful API design.

In Ivy, state lives on the server. The browser only sees rendered widgets — never your business logic, database queries, or sensitive data. Updates to the widget tree is sent as patches efficiently over websocket.

Layout Composition

React developers love composition. Ivy uses the | operator for the same pattern:

React:

<Layout direction="vertical" gap={4}>
  <Header />
  <Sidebar />
  <Content />
</Layout>

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.

Event Handling

React's event model translates directly:

React:

<input
  value={search}
  onChange={(e) => setSearch(e.target.value)}
/>
<button onClick={() => handleSubmit()}>
  Submit
</button>

Ivy:

var search = UseState("");

return Layout.Verical() 
  | search.ToTextInput();
  | new Button("Submit", onClick: _ => HandleSubmit(search.Value))

Same concepts: controlled inputs, event handlers, state updates triggering re-renders.

Context and Services

React:

const theme = useContext(ThemeContext);

Ivy:

var theme = UseService<IThemeProvider>();

Ivy uses .NET's built-in dependency injection, which is more powerful than React Context for complex applications.

Async

Ivy handles async naturally:

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 React 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 trying to replace React—it's bringing React's brilliant component model to the .NET ecosystem. If your team knows C# and you're tired of maintaining separate frontend and backend codebases, Ivy lets you use your React intuition without the JavaScript toolchain complexity.

Your mental model transfers. Your patterns transfer. You just write C# instead.

Resources: