AI applications are moving beyond simple chat interfaces.

Modern AI agents can call tools, retrieve information, execute workflows, ask for additional input, and perform multiple steps before producing a final response. However, many applications still present all of that activity through a basic text box and a stream of messages.

That creates a disconnect between what the agent is doing and what the user can see.

A better approach is to build an agentic UI where the interface reacts to the agent's state, tool calls, progress, results, approvals, and errors.

For .NET developers, Blazor provides a practical way to build these experiences because application logic and UI components can be developed using C#. When combined with AI components and an agent orchestration layer, Blazor can provide an interactive interface for agent-driven applications.

This article explains the architecture behind agentic UIs and demonstrates how to build one using Blazor-style components and C#.

What Is an Agentic UI?

A traditional AI chat application usually follows a simple interaction model:

User
  |
  v
Chat Input
  |
  v
AI Model
  |
  v
Text Response
  |
  v
Chat Window

An agentic application is more dynamic.

The agent may:

  1. Receive a user request.

  2. Determine what action is required.

  3. Select one or more tools.

  4. Execute those tools.

  5. Inspect the results.

  6. Decide whether another action is necessary.

  7. Return a final response.

The UI should represent this process.

A simplified architecture looks like this:

                 User
                   |
                   v
          +------------------+
          |   Blazor UI      |
          +------------------+
                   |
                   v
          +------------------+
          | Agent Orchestrator|
          +------------------+
             /      |       \
            v       v        v
         Search   Database   API
            |       |        |
            +-------+--------+
                    |
                    v
              Agent Result
                    |
                    v
              Blazor UI

Instead of showing only the final answer, the application can display useful intermediate states.

For example:

Analyzing request...
Searching customer records...
Checking order status...
Preparing response...

This makes the application feel more transparent and responsive.

Why Blazor Works Well for Agentic Applications

Blazor provides component-based UI development using .NET and C#.

That becomes particularly useful when the AI orchestration layer is also implemented in .NET.

A typical application can be organized like this:

Blazor Components
       |
       v
Application Services
       |
       v
Agent Orchestration
       |
       +---- LLM
       |
       +---- Tools
       |
       +---- APIs
       |
       +---- Databases

The UI does not need to know how every tool works.

Instead, it consumes application state such as:

public class AgentState
{
    public string Status { get; set; } = "Idle";

    public string CurrentStep { get; set; } = string.Empty;

    public List<string> Messages { get; set; } = new();

    public bool WaitingForApproval { get; set; }

    public string? FinalResponse { get; set; }
}

The component can then render the state dynamically.

Designing the Agentic UI

A good agentic UI should expose the information users actually need.

A useful layout can contain four areas:

+------------------------------------------------+
|                 AI Assistant                   |
+------------------------------------------------+
|                                                |
| User Request                                   |
| "Find delayed orders for customer 1042"        |
|                                                |
| Agent Activity                                 |
| ✓ Customer identified                          |
| ✓ Order database searched                      |
| ● Checking shipment status                     |
|                                                |
| Tool Results                                   |
| 3 delayed orders found                         |
|                                                |
| Agent Response                                 |
| "I found 3 delayed orders..."                  |
+------------------------------------------------+

The UI can use separate components for each responsibility.

For example:

AgentChat
 ├── MessageList
 ├── AgentStatus
 ├── ToolActivity
 ├── ToolResult
 ├── ApprovalPanel
 └── ResponsePanel

This is easier to maintain than putting the entire agent experience into one large component.

Creating a Basic Agent Service

Start by defining an application service responsible for running the agent.

public interface IAgentService
{
    Task<AgentResponse> RunAsync(
        string request,
        CancellationToken cancellationToken = default);
}

A simple response model could be:

public class AgentResponse
{
    public string Message { get; set; } = string.Empty;

    public List<AgentStep> Steps { get; set; } = new();
}

The step model describes what happened during execution.

public class AgentStep
{
    public string Name { get; set; } = string.Empty;

    public string Status { get; set; } = string.Empty;

    public string? Result { get; set; }
}

In a real application, the agent service would connect to your selected AI model and tool infrastructure.

The important design principle is to keep that implementation separate from the UI.

Building the Blazor Component

A simple Blazor component can maintain the current request and agent state.

@page "/agent"

<h2>AI Agent</h2>

<div class="agent-container">

    <input @bind="request"
           placeholder="Ask the agent something..." />

    <button @onclick="RunAgent"
            disabled="@isRunning">
        @(isRunning ? "Working..." : "Run Agent")
    </button>

    @if (isRunning)
    {
        <p>@currentStep</p>
    }

    @if (response is not null)
    {
        <div class="agent-response">
            @response.Message
        </div>
    }

</div>

@code {
    private string request = string.Empty;
    private string currentStep = "Starting...";
    private bool isRunning;

    private AgentResponse? response;

    private async Task RunAgent()
    {
        if (string.IsNullOrWhiteSpace(request))
            return;

        isRunning = true;
        currentStep = "Analyzing request...";

        try
        {
            response = await AgentService.RunAsync(request);
        }
        finally
        {
            isRunning = false;
            currentStep = "Completed";
        }
    }
}

This is intentionally simple.

Production applications should provide richer state management and streaming updates, but the component demonstrates the core pattern.

Showing Agent Progress

One of the biggest differences between a chatbot and an agentic UI is progress visibility.

Instead of waiting silently for a potentially long-running operation, show the current operation.

For example:

public class AgentStep
{
    public string Name { get; set; } = string.Empty;

    public AgentStepStatus Status { get; set; }

    public DateTimeOffset StartedAt { get; set; }

    public DateTimeOffset? CompletedAt { get; set; }
}

An enumeration can make the UI logic easier to understand.

public enum AgentStepStatus
{
    Pending,
    Running,
    Completed,
    Failed
}

The Blazor component can translate these states into visual indicators.

@foreach (var step in steps)
{
    <div class="agent-step">
        <strong>@step.Name</strong>

        @switch (step.Status)
        {
            case AgentStepStatus.Pending:
                <span>Waiting</span>
                break;

            case AgentStepStatus.Running:
                <span>Running...</span>
                break;

            case AgentStepStatus.Completed:
                <span>Completed</span>
                break;

            case AgentStepStatus.Failed:
                <span>Failed</span>
                break;
        }
    </div>
}

This gives users visibility into the agent's workflow without exposing unnecessary internal reasoning.

Tool Calls Should Become UI Events

An agent may use tools such as:

  • Customer lookup

  • Product search

  • Database queries

  • Document retrieval

  • Calendar APIs

  • Internal business APIs

The UI can represent tool execution as structured activity.

For example:

public record ToolActivity(
    string ToolName,
    string Status,
    DateTimeOffset Timestamp);

When a tool starts:

Searching customer database...

When it finishes:

Customer database search completed.

The application can maintain these events independently from the final response.

This distinction is important because tool execution is application state, while the final response is user-facing content.

Adding Human Approval

Not every agent action should happen automatically.

For sensitive operations such as:

  • Sending an email

  • Creating an order

  • Deleting data

  • Updating financial information

  • Publishing content

the agent can pause and ask the user for approval.

The state might look like this:

public class ApprovalRequest
{
    public string Action { get; set; } = string.Empty;

    public string Description { get; set; } = string.Empty;

    public bool IsApproved { get; set; }
}

The Blazor UI can display:

Agent wants to send this email:

To: [email protected]
Subject: Order Update

[Approve] [Reject]

The agent should not perform the action until the application receives the approval.

This creates a useful boundary:

AI decides what could be done
             |
             v
Application checks permissions
             |
             v
User approves sensitive action
             |
             v
Tool executes action

The application, rather than the model, should remain responsible for authorization.

Streaming Agent Responses

Long-running agents benefit from incremental updates.

Instead of waiting for the entire operation to finish, the server can stream status events to the Blazor client.

Conceptually:

Agent
  |
  +--> Started
  |
  +--> Tool: SearchDocuments
  |
  +--> Tool Completed
  |
  +--> Generating Response
  |
  +--> Completed

The UI can update after each event.

A simple event model could be:

public record AgentEvent(
    string Type,
    string Message);

For example:

yield return new AgentEvent(
    "status",
    "Searching documents...");

yield return new AgentEvent(
    "tool",
    "Document search completed.");

yield return new AgentEvent(
    "response",
    "I found three matching documents.");

The exact transport mechanism depends on the application architecture. Server-side streaming, SignalR, or another event-based mechanism can be used when the application requires continuous updates.

Handling Errors

Agentic applications have more failure points than simple chat applications.

A request can fail because:

  • The model is unavailable.

  • A tool times out.

  • An API returns an error.

  • Authentication expires.

  • A database query fails.

  • The agent exceeds a configured execution limit.

The UI should distinguish these states.

For example:

Agent Activity

✓ Customer lookup
✓ Order search
✕ Shipping API

The shipping service did not respond.
[Retry]

Avoid exposing raw exception messages directly to users.

Instead, log detailed diagnostic information on the server and display an actionable message in the UI.

Separating Agent State From UI State

A common architectural mistake is putting too much agent logic directly into a .razor component.

For example, avoid turning a component into a combination of:

UI
+
Prompt Construction
+
Tool Execution
+
Database Access
+
Authorization
+
Agent Orchestration

A cleaner structure is:

Components
    |
    v
UI/Application State
    |
    v
Agent Service
    |
    +---- Model Provider
    |
    +---- Tool Registry
    |
    +---- Business Services
    |
    +---- Data Access

This makes the application easier to test and evolve.

Agentic UI vs Traditional Chat UI

Capability

Traditional Chat UI

Agentic UI

Text conversation

Yes

Yes

Agent progress

Limited

Yes

Tool activity

Usually hidden

Visible

Multi-step workflows

Limited

Supported

Human approval

Basic

First-class interaction

Error states

Simple

Workflow-aware

Structured results

Limited

Strong

Long-running operations

Difficult to represent

Natural

The goal is not to expose every internal operation.

The goal is to expose the right state at the right time.

Common Mistakes

Showing Too Much Internal Information

Users do not need every internal model operation.

Expose meaningful events such as:

Searching documents...
Checking order status...
Waiting for approval...

rather than dumping internal prompts or implementation details.

Letting the UI Control Authorization

A hidden button or disabled UI control is not a security boundary.

Authorization must be enforced on the server.

Treating Every Agent Step as Successful

Tool calls can fail independently.

Represent Pending, Running, Completed, and Failed states explicitly.

Creating One Giant Component

Large agent components quickly become difficult to maintain.

Break the experience into reusable components such as:

AgentChat
AgentTimeline
ToolActivity
ApprovalDialog
AgentResult

Ignoring Cancellation

Users may close a page or cancel a long-running request.

Agent services should support cancellation where practical.

Task<AgentResponse> RunAsync(
    string request,
    CancellationToken cancellationToken);

This helps prevent unnecessary work after the user has abandoned the operation.

Best Practices

When building agentic UIs with Blazor, consider these practices:

  1. Keep AI orchestration outside UI components.

  2. Represent agent execution as explicit state.

  3. Show useful progress for long-running operations.

  4. Treat tool calls as structured application events.

  5. Require approval for sensitive actions.

  6. Keep authorization on the server.

  7. Support cancellation for long-running workflows.

  8. Handle tool failures independently from model failures.

  9. Use reusable Blazor components for different agent states.

  10. Stream updates when workflows take long enough to benefit from them.

  11. Avoid exposing internal prompts or sensitive implementation details.

  12. Log detailed diagnostic information separately from the user-facing interface.

A Practical Project Structure

A maintainable Blazor agent application might look like this:

MyAgentApp/
│
├── Components/
│   ├── AgentChat.razor
│   ├── AgentTimeline.razor
│   ├── ToolActivity.razor
│   ├── ApprovalPanel.razor
│   └── AgentResponse.razor
│
├── Services/
│   ├── AgentService.cs
│   ├── ToolService.cs
│   └── ApprovalService.cs
│
├── Models/
│   ├── AgentState.cs
│   ├── AgentStep.cs
│   ├── AgentEvent.cs
│   └── ApprovalRequest.cs
│
└── Program.cs

This separation keeps presentation, orchestration, and domain behavior independent.

Conclusion

Agentic applications require a different UI approach from traditional chatbots.

The agent may perform several operations before producing an answer, and users benefit from seeing meaningful progress, tool activity, errors, approvals, and structured results.

Blazor is well suited to this model because its component architecture makes it possible to build reusable UI elements around agent state while keeping the underlying orchestration in C# services.

The most important design principle is simple:

Don't build a chatbot that happens to call tools.

Build an application UI that understands
the state of an AI-driven workflow.

Once agent execution is represented as structured state and events, features such as progress indicators, approval workflows, streaming updates, retries, and structured results become much easier to implement.

Summary

A Blazor-based agentic UI can provide a richer experience for AI applications by connecting agent execution state with interactive components. Instead of displaying only a final model response, the application can show meaningful workflow progress, tool activity, approval requests, failures, and results.

The best architecture keeps the Blazor UI focused on presentation while dedicated services handle agent orchestration, tools, authorization, and business logic. This separation makes the application easier to test, secure, and extend as AI workflows become more sophisticated.