.NETBlazorAI AgentsTutorial

Build an AI Chatbot in Blazor with .NET

A practical guide to building an AI chatbot in Blazor: wire up a chat UI, stream the model's response token-by-token, and keep the conversation in state — all in C#.

A chatbot is the “hello world” of AI apps — and Blazor is a natural fit because you can build the whole thing, UI and model calls, in C#. This guide builds a working AI chat component in Blazor: a message list, an input box, streamed responses that appear token-by-token, and conversation state held across turns. No JavaScript required.

It assumes you can already make a model call — if not, start with how to call the OpenAI API from C#.

The shape of the app

A chat UI needs three things: a list of messages to render, a way to send a new message, and a way to stream the reply in as it generates. We’ll use Microsoft.Extensions.AI’s IChatClient so the same component works with OpenAI, Azure OpenAI, or a local model via Ollama.

Register the chat client in Program.cs:

builder.Services.AddSingleton<IChatClient>(sp =>
    new OpenAIClient(Environment.GetEnvironmentVariable("OPENAI_API_KEY"))
        .GetChatClient("gpt-4o-mini")
        .AsIChatClient());

The chat component

Here’s the core of a Chat.razor component — a message list, an input, and a send handler that streams the response into the last message as tokens arrive:

@page "/chat"
@inject IChatClient ChatClient
@using Microsoft.Extensions.AI

<div class="messages">
    @foreach (var msg in _messages)
    {
        <div class="msg @msg.Role.ToString().ToLower()">@msg.Text</div>
    }
</div>

<input @bind="_input" @bind:event="oninput" @onkeyup="OnKey" placeholder="Ask something..." />
<button @onclick="Send" disabled="@_busy">Send</button>

@code {
    private readonly List<ChatMessage> _messages = new();
    private string _input = "";
    private bool _busy;

    private async Task Send()
    {
        if (string.IsNullOrWhiteSpace(_input) || _busy) return;
        _busy = true;

        _messages.Add(new(ChatRole.User, _input));
        _input = "";

        var assistant = new ChatMessage(ChatRole.Assistant, "");
        _messages.Add(assistant);

        // Stream the reply straight into the assistant message
        await foreach (var update in ChatClient.GetStreamingResponseAsync(_messages))
        {
            assistant.Text += update.Text;
            StateHasChanged();          // re-render as each chunk arrives
        }

        _busy = false;
    }

    private async Task OnKey(KeyboardEventArgs e)
    {
        if (e.Key == "Enter") await Send();
    }
}

The key move is StateHasChanged() inside the streaming loop — that re-renders the component on each chunk, so users watch the answer type itself out. Passing the whole _messages list each turn is what gives the bot memory of the conversation.

Why this is clean in Blazor

Notice what’s not here: no fetch, no JSON wrangling, no separate API endpoint, no client-side JavaScript. The component calls the model directly in C#, streams into a field, and Blazor’s rendering handles the UI. For Blazor Server, the streaming updates flow to the browser over the existing SignalR circuit automatically. (For a browser-to-server streaming pattern in other setups, see streaming responses to a web UI.)

Making it production-ready

A demo chatbot becomes a real feature when you add:

  • A system prompt — seed _messages with a ChatRole.System message to set the bot’s behaviour and scope.
  • Tools — let the bot do things, not just talk, by turning it into a tool-using agent.
  • History limits — cap or summarize old turns so the prompt doesn’t grow unbounded (see agent memory and cost control).
  • Auth and guardrails — a chatbot that calls tools is an attack surface; see securing AI agents.

Note: Microsoft.Extensions.AI method names (GetStreamingResponseAsync, AsIChatClient) are still settling; verify against the current .NET AI docs for your package version. The pattern — inject IChatClient, stream into a message, StateHasChanged per chunk — is the durable part.

Takeaway

Blazor makes an AI chatbot refreshingly simple: inject an IChatClient, render a message list, and stream the reply into the last message with a StateHasChanged() on each chunk — all in C#, no JavaScript. Build it on IChatClient and you can point it at OpenAI, Azure, or a local model unchanged. Add a system prompt, tools, and history limits, and your “hello world” chatbot becomes a real product feature.


Have a correction or a topic you want covered? Email mani.bc72@gmail.com.