# Stack Research **Domain:** Blazor WebAssembly AI Chat Application (.NET / C#) **Researched:** 2026-03-27 **Confidence:** HIGH (core stack verified via NuGet and official Microsoft docs; version numbers confirmed via nuget.org) --- ## Recommended Stack ### Core Technologies | Technology | Version | Purpose | Why Recommended | |------------|---------|---------|-----------------| | .NET 9 SDK | 9.x (latest patch) | Runtime, tooling, SDK | LTS-adjacent, stable, .NET 10 is in preview — stay on 9 for a tutorial project targeting a stable foundation | | Blazor WebAssembly Standalone | .NET 9 | Client SPA running in-browser | Non-negotiable per project constraints; client-side execution with no server round-trip for UI | | ASP.NET Core Web API | .NET 9 | Backend proxy for OpenAI calls | Required to keep the OpenAI API key server-side; WASM cannot access secrets directly | | C# 13 | Included with .NET 9 | Application language | Included in .NET 9 SDK; no separate install needed | **Critical architecture note:** The "hosted Blazor WebAssembly" template (single `.sln` with Client + Server + Shared projects) was removed in .NET 8. In .NET 9, you create two separate projects manually: a `dotnet new blazorwasm` standalone client and a `dotnet new webapi` backend, then add them to a solution. This is the correct approach for this project. ### OpenAI Integration | Library | Version | Purpose | Why Recommended | |---------|---------|---------|-----------------| | `OpenAI` (official) | 2.9.1 | OpenAI API client with streaming | The official OpenAI-published .NET library; supports `CompleteChatStreamingAsync()` returning `AsyncCollectionResult` via `await foreach`; stable release as of 2026-03-02 | **Do not use** `OpenAI-DotNet` (version 8.8.8) — this is an unofficial community package with a different API surface. The official `OpenAI` package is published directly by OpenAI and is the correct choice. **Streaming mechanism:** The backend Web API endpoint calls `CompleteChatStreamingAsync()` and proxies chunks to the client. The WASM client uses `HttpCompletionOption.ResponseHeadersRead` with `SetBrowserResponseStreamingEnabled(true)` on the `HttpRequestMessage` to consume the streamed response. In .NET 10 streaming is enabled by default; in .NET 9 it must be explicitly opted in per-request. ### Markdown Rendering | Library | Version | Purpose | Why Recommended | |---------|---------|---------|-----------------| | `Markdig` | 1.1.1 | Parse markdown text to HTML | The de facto standard markdown processor for .NET; CommonMark-compliant, fast, extensible, targets .NET Standard 2.0 so works in WASM; used by Microsoft and Syncfusion as the underlying engine | **How it integrates in Blazor:** Call `Markdig.Markdown.ToHtml(content)` on the client, render the result with `@((MarkupString)htmlContent)` in a Razor component. No JS interop needed. ### UI Component Library | Library | Version | Purpose | Why Recommended | |---------|---------|---------|-----------------| | `MudBlazor` | 9.2.0 | Material Design component library | Full .NET 9 support confirmed; pure C# with minimal JavaScript; comprehensive chat-friendly components (MudTextField, MudPaper, MudScrollToBottom, MudList); large community; no per-seat licensing | **Alternative considered:** Radzen Blazor (free, good) and Telerik UI for Blazor (licensed). MudBlazor wins for a tutorial/personal project because it is free, has zero JS dependencies, and has excellent documentation for learners. ### JSON Storage (Server-side) | Technology | Version | Purpose | Why Recommended | |------------|---------|---------|-----------------| | `System.Text.Json` | Built into .NET 9 | Serialize/deserialize conversation history | Built-in, no extra dependency; `JsonSerializerOptions` with `WriteIndented = true` for human-readable files; async file I/O via `File.ReadAllTextAsync` / `File.WriteAllTextAsync` | Storage lives entirely on the **backend** (Web API project). The WASM client cannot access the local filesystem — only the server can. API endpoints expose CRUD operations over conversations, with JSON files persisted in a configurable directory on the server host. --- ## Supporting Libraries | Library | Version | Purpose | When to Use | |---------|---------|---------|-------------| | `Microsoft.Extensions.AI` (abstractions) | 9.x preview | Optional AI abstraction layer | Skip for v1 — adds indirection before the core chat pattern is understood. Relevant for v2 when adding multi-provider support | | `Blazored.LocalStorage` | latest | Browser local storage | Not needed for this project — persistence is on the server via JSON files, not the browser | | `System.Net.ServerSentEvents` | Built into .NET 9 | SSE parser for streaming | Used automatically by the `OpenAI` library on the server; no direct usage needed | --- ## Development Tools | Tool | Purpose | Notes | |------|---------|-------| | Visual Studio 2022 (v17.12+) | IDE with Blazor hot reload | Recommended for tutorial builder; full Blazor debugging, component preview, and hot reload support | | VS Code + C# Dev Kit | Lighter-weight alternative | Works well; use `dotnet watch` for hot reload | | `dotnet watch run` | Hot reload during development | Run in both Client and Server project directories simultaneously | | `dotnet-dev-certs` | HTTPS dev certificate | Required for local HTTPS; run `dotnet dev-certs https --trust` once | --- ## Installation ```bash # Create solution mkdir ChatAgentApp && cd ChatAgentApp dotnet new sln -n ChatAgentApp # Create Blazor WASM client (standalone) dotnet new blazorwasm -n ChatAgentApp.Client --framework net9.0 dotnet sln add ChatAgentApp.Client/ChatAgentApp.Client.csproj # Create ASP.NET Core Web API backend dotnet new webapi -n ChatAgentApp.Api --framework net9.0 dotnet sln add ChatAgentApp.Api/ChatAgentApp.Api.csproj # Install OpenAI SDK in the API project cd ChatAgentApp.Api dotnet add package OpenAI --version 2.9.1 # Install Markdig in the Client project cd ../ChatAgentApp.Client dotnet add package Markdig --version 1.1.1 dotnet add package MudBlazor --version 9.2.0 ``` --- ## Alternatives Considered | Recommended | Alternative | When to Use Alternative | |-------------|-------------|-------------------------| | `OpenAI` 2.9.1 (official) | `OpenAI-DotNet` 8.8.8 (unofficial) | Never — the official package is now stable and maintained by OpenAI directly | | `OpenAI` 2.9.1 (official) | `Azure.AI.OpenAI` 2.1.0 | When targeting Azure OpenAI Service specifically (e.g., enterprise, EU data residency, private endpoints) — overkill for this project | | `Markdig` | `CommonMark.NET` | Only if strict CommonMark compliance matters more than extensions; Markdig is a superset and the ecosystem standard | | `MudBlazor` | Radzen Blazor | Radzen is fine; choose it if you already know it; MudBlazor has more learning resources | | `MudBlazor` | Telerik UI for Blazor | Telerik requires a paid license; not appropriate for a personal tool | | Standalone WASM + separate Web API | Blazor Web App template (unified) | Use the unified Blazor Web App template when you want mixed Server+WASM render modes on a single project; overkill for this project and obscures the WASM-specific patterns the tutorial aims to teach | | JSON flat files (server-side) | SQLite via EF Core | SQLite is a better choice at scale; JSON is simpler for single-user personal tools and avoids introducing a migration workflow | --- ## What NOT to Use | Avoid | Why | Use Instead | |-------|-----|-------------| | `OpenAI-DotNet` (unofficial) | Different API surface, not maintained by OpenAI, version numbers create confusion | Official `OpenAI` NuGet package | | `Microsoft.SemanticKernel` | Adds significant abstraction and dependency weight for a tutorial; streaming works but is complex to explain | Direct `OpenAI` SDK calls; add SK in v2 when orchestration is needed | | JavaScript `EventSource` API via JSInterop for streaming | Blazor WASM has `SetBrowserResponseStreamingEnabled` which avoids JS interop; adding JSInterop for streaming increases complexity significantly | `HttpCompletionOption.ResponseHeadersRead` + `SetBrowserResponseStreamingEnabled(true)` in the HTTP handler | | `Newtonsoft.Json` | Unnecessary dependency; `System.Text.Json` is built into .NET 9 and is faster; Newtonsoft was the pre-.NET Core standard | `System.Text.Json` (built-in) | | `Blazored.LocalStorage` for persistence | Browser storage is limited (~5MB), cleared by users, and not suitable for chat history of any meaningful length; also exposes all data client-side | Server-side JSON file storage via the Web API | | AOT compilation during learning phase | Dramatically increases build times; not needed until production optimization is a concern; confusing to introduce in a tutorial | Default IL interpretation; add AOT opt-in note in the final phase | --- ## Stack Patterns by Variant **For streaming responses from the API backend to the WASM client:** - Backend streams OpenAI tokens as `text/event-stream` (SSE) or `application/x-ndjson` - Client uses `SetBrowserResponseStreamingEnabled(true)` on `HttpRequestMessage` - Client reads with `HttpCompletionOption.ResponseHeadersRead` and iterates the stream - Trigger `StateHasChanged()` in the component after each token to update the UI **For local JSON file storage on the server:** - Define a `ConversationRepository` service on the API that reads/writes from a configurable base path - Register as `Singleton` (not `Scoped`) since there is only one user and file access must be serialized - Use `SemaphoreSlim(1,1)` to prevent concurrent write conflicts even in single-user mode **For markdown rendering in the client:** - Use `Markdig.Markdown.ToHtml(text, pipeline)` where `pipeline` is built with `MarkdownPipelineBuilder` enabling extensions (e.g., `UseAutoLinks()`, `UseEmojiAndSmiley()`) - Render the HTML string using `@((MarkupString)html)` inside a `
` element - Apply CSS (GitHub Markdown CSS or custom) scoped to `.markdown-body` for code blocks and tables --- ## Version Compatibility | Package | Compatible With | Notes | |---------|-----------------|-------| | `OpenAI` 2.9.1 | .NET Standard 2.0+ (.NET 9 confirmed) | Published 2026-03-02; requires `System.Net.ServerSentEvents` (built into .NET 9) | | `Markdig` 1.1.1 | .NET 8.0, .NET Standard 2.0, .NET Framework 4.6.2 | .NET 9 compatible via .NET 8 TFM; published 2026-03-04 | | `MudBlazor` 9.2.0 | .NET 8.0, .NET 9.0, .NET 10.0 | Published 2026-03-18; version 9.x = full support for .NET 9 | | .NET 9 SDK | Blazor WASM + Web API in same solution | Both project types target `net9.0`; no cross-framework issues | --- ## Sources - https://www.nuget.org/packages/OpenAI — Official OpenAI NuGet package; version 2.9.1 confirmed (2026-03-02) - https://github.com/openai/openai-dotnet — Official OpenAI .NET SDK; streaming API verified (`CompleteChatStreamingAsync`, `await foreach`) - https://www.nuget.org/packages/Markdig — Markdig version 1.1.1 confirmed (2026-03-04) - https://www.nuget.org/packages/MudBlazor — MudBlazor 9.2.0 confirmed; .NET 8/9/10 full support (2026-03-18) - https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models?view=aspnetcore-9.0 — Official Blazor hosting model docs; standalone WASM vs Blazor Web App distinction verified - https://learn.microsoft.com/en-us/dotnet/core/compatibility/networking/10.0/default-http-streaming — Breaking change: WASM streaming opt-in (.NET 9) vs default (.NET 10) - https://www.strathweb.com/2024/07/built-in-support-for-server-sent-events-in-net-9/ — SSE native support in .NET 9 via `System.Net.ServerSentEvents`; used internally by OpenAI SDK (MEDIUM confidence, single source) - https://github.com/openai/openai-dotnet/issues/65 — Confirmed streaming issue in Blazor WASM requires `SetBrowserResponseStreamingEnabled(true)` (MEDIUM confidence, GitHub issue thread) - https://devblogs.microsoft.com/dotnet/openai-dotnet-library/ — Official .NET Blog announcement of the OpenAI library - https://dev.to/kazinix/blazor-web-app-webassembly-hosted-in-net8-and-net9-1k6g — Hosted template removal in .NET 8+, manual solution structure (MEDIUM confidence) --- *Stack research for: Blazor WebAssembly AI Chat Application* *Researched: 2026-03-27*