What is Blazor?

Blazor is a framework for building interactive web UIs using C# instead of JavaScript. Imagine being able to write your entire web application - frontend and backend - in the same language (C#). That's Blazor's superpower!

Blazor lets you build rich, interactive web applications using .NET and C#, with code that runs either in the browser via WebAssembly or on the server with real-time updates via SignalR.

Why Use Blazor?

Think of learning a new spoken language. With traditional web dev, you learn C# for backend and JavaScript for frontend - two languages. Blazor lets you use just C# for everything:

  • One Language, Full Stack: Write C# for both client and server - no context switching between languages
  • Share Code: Reuse models, validation logic, and utilities between client and server
  • Type Safety: Catch errors at compile-time instead of runtime in the browser
  • Performance: WebAssembly runs near-native speed; Server mode reduces client download size
  • Leverage .NET Ecosystem: Use NuGet packages, LINQ, async/await - all the C# features you know
  • Strong Tooling: Visual Studio IntelliSense, debugging, and refactoring work for frontend code too
  • SEO-Friendly: Server-side rendering available for search engine optimization

Blazor WebAssembly vs Blazor Server

Blazor comes in two flavors, like having ice cream delivered (Server) vs making it yourself (WebAssembly):

BLAZOR WEBASSEMBLY (Client-Side)
─────────────────────────────────
How it works:
1. Downloads .NET runtime + your app to browser
2. Runs entirely in browser using WebAssembly
3. No server connection needed after initial load

Think of it as: Installing a desktop app in your browser

Pros:
✅ Works offline after initial load
✅ Reduced server load (logic runs on client)
✅ Can be hosted on static file servers (CDN)
✅ Fast after initial load

Cons:
⚠️ Large initial download (2-3 MB for .NET runtime)
⚠️ Slower initial load time
⚠️ No access to server resources directly
⚠️ Code visible to users (can be decompiled)

Best for:
- Progressive Web Apps (PWAs)
- Apps that need offline support
- Apps with heavy client-side logic
- Public-facing applications

---

BLAZOR SERVER (Server-Side)
───────────────────────────
How it works:
1. Small JavaScript downloads to browser
2. Establishes SignalR connection to server
3. UI events sent to server, DOM updates sent back

Think of it as: Remote desktop for your app

Pros:
✅ Small download size (~250 KB)
✅ Fast initial load
✅ Full .NET runtime on server
✅ Code stays on server (secure)
✅ Works on older browsers

Cons:
⚠️ Requires constant server connection
⚠️ Higher server resource usage
⚠️ Latency for every interaction
⚠️ Doesn't work offline

Best for:
- Internal enterprise apps
- Apps with sensitive business logic
- Apps targeting older devices
- Rapid prototyping

---

HYBRID APPROACH
───────────────
Blazor Auto (.NET 8+) can switch between Server and WebAssembly automatically!

When to Use Blazor?

Blazor is perfect for:

  • .NET Teams: Your team already knows C# and .NET - no need to learn JavaScript frameworks
  • Enterprise Internal Apps: Admin panels, dashboards, management tools where offline support isn't critical
  • Code Sharing: Need to share models, validation, and logic between client and server
  • Progressive Web Apps: With Blazor WebAssembly for offline-capable applications
  • Rapid Prototyping: Blazor Server lets you build and test quickly

When NOT to use Blazor:

  • SEO is critical (use React with SSR, Next.js, or Blazor with pre-rendering)
  • Team is experienced in React/Vue/Angular but not .NET
  • Need maximum performance for animations/games (use vanilla JS or React)
  • Building mobile apps primarily (use React Native or .NET MAUI)

Blazor Components: Building Blocks

Components are like LEGO blocks - reusable pieces of UI. Here's a simple component:

@* Counter.razor - A simple component *@

Counter

Current count: @currentCount

@code { private int currentCount = 0; private void IncrementCount() { currentCount++; } } --- @* ProductCard.razor - Component with parameters *@

@Product.Name

@Product.Price.ToString("C")

@code { // Input parameter [Parameter] public Product Product { get; set; } // Output event [Parameter] public EventCallback OnAddToCart { get; set; } } --- @* Using the component in a page *@ @page "/products"

Our Products

@foreach (var product in products) { } @code { private List products = new(); protected override async Task OnInitializedAsync() { products = await ProductService.GetProductsAsync(); } private void HandleAddToCart(Product product) { // Add to cart logic Console.WriteLine($"Added {product.Name} to cart"); } }

Data Binding: Connecting UI and Code

@* ONE-WAY BINDING (display data) *@

Hello, @userName!

Price: @product.Price.ToString("C")

--- @* TWO-WAY BINDING (form inputs) *@

You typed: @userName

@* Bind with event *@ @code { private string userName = "Guest"; private string searchTerm = ""; } --- @* BINDING TO COMPLEX OBJECTS *@
@code { private Customer customer = new(); private async Task HandleSubmit() { await CustomerService.SaveAsync(customer); // Show success message } } --- @* EVENT HANDLING *@ @code { private void HandleClick() { Console.WriteLine("Button clicked!"); } private void HandleClickWithParameter(int id) { Console.WriteLine($"Clicked with id: {id}"); } private async Task HandleAsyncClick() { await Task.Delay(1000); Console.WriteLine("Async operation completed"); } }

Component Lifecycle

Components have a lifecycle, like a plant growing: seed → sprout → flower → wither. Here are the key stages:

@code {
    // 1. CONSTRUCTOR - Component created (rarely used)
    public MyComponent()
    {
        // Avoid doing work here
    }

    // 2. SET PARAMETERS - Parameters assigned
    public override void SetParametersAsync(ParameterView parameters)
    {
        // Advanced: intercept parameter setting
    }

    // 3. INITIALIZED - Component initialized (before first render)
    protected override void OnInitialized()
    {
        // Initialize data
        userName = "Guest";
    }

    // ASYNC VERSION (most commonly used)
    protected override async Task OnInitializedAsync()
    {
        // Load data from API
        products = await ProductService.GetProductsAsync();
    }

    // 4. PARAMETERS SET - After parameters are set
    protected override void OnParametersSet()
    {
        // React to parameter changes
        if (ProductId != previousProductId)
        {
            // Product changed, reload data
        }
    }

    protected override async Task OnParametersSetAsync()
    {
        await LoadProductAsync(ProductId);
    }

    // 5. AFTER RENDER - After component rendered to DOM
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            // Run once after first render
            // Good for JavaScript interop
        }
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync("initializeChart", chartData);
        }
    }

    // 6. DISPOSE - Component removed from UI
    public void Dispose()
    {
        // Clean up resources
        // Unsubscribe from events
        timer?.Dispose();
    }
}

Dependency Injection in Blazor

// REGISTER SERVICES (Program.cs)
builder.Services.AddScoped();
builder.Services.AddHttpClient(client =>
{
    client.BaseAddress = new Uri("https://api.example.com");
});

---

// INJECT IN COMPONENT
@inject IProductService ProductService
@inject NavigationManager Navigation
@inject IJSRuntime JSRuntime

Products

@if (products == null) {

Loading...

} else { @foreach (var product in products) {
@product.Name - @product.Price.ToString("C")
} } @code { private List products; protected override async Task OnInitializedAsync() { products = await ProductService.GetAllAsync(); } private void NavigateToDetails(int id) { Navigation.NavigateTo($"/products/{id}"); } private async Task ShowAlert() { await JSRuntime.InvokeVoidAsync("alert", "Hello from Blazor!"); } }

Routing and Navigation

@* BASIC ROUTING *@
@page "/products"

All Products

--- @* ROUTE WITH PARAMETER *@ @page "/products/{id:int}"

Product Details - ID: @Id

@code { [Parameter] public int Id { get; set; } protected override async Task OnParametersSetAsync() { product = await ProductService.GetByIdAsync(Id); } } --- @* MULTIPLE ROUTES *@ @page "/products" @page "/items" @page "/shop" --- @* OPTIONAL PARAMETER *@ @page "/search/{searchTerm?}" @code { [Parameter] public string SearchTerm { get; set; } } --- @* NAVIGATION *@ @inject NavigationManager Navigation @code { private void NavigateToProduct() { Navigation.NavigateTo($"/products/{productId}"); } } --- @* NAVIGATION LINKS *@ Products Product 123 @* Active class automatically applied when route matches *@

Calling APIs from Blazor

// PRODUCT SERVICE
public class ProductService
{
    private readonly HttpClient _http;

    public ProductService(HttpClient http)
    {
        _http = http;
    }

    public async Task> GetAllAsync()
    {
        return await _http.GetFromJsonAsync>("api/products");
    }

    public async Task GetByIdAsync(int id)
    {
        return await _http.GetFromJsonAsync($"api/products/{id}");
    }

    public async Task CreateAsync(Product product)
    {
        var response = await _http.PostAsJsonAsync("api/products", product);
        return await response.Content.ReadFromJsonAsync();
    }

    public async Task UpdateAsync(int id, Product product)
    {
        await _http.PutAsJsonAsync($"api/products/{id}", product);
    }

    public async Task DeleteAsync(int id)
    {
        await _http.DeleteAsync($"api/products/{id}");
    }
}

---

// USE IN COMPONENT
@inject IProductService ProductService

@if (isLoading)
{
    

Loading...

} else if (error != null) {

Error: @error

} else {
    @foreach (var product in products) {
  • @product.Name - @product.Price.ToString("C")
  • }
} @code { private List products; private bool isLoading = true; private string error; protected override async Task OnInitializedAsync() { try { products = await ProductService.GetAllAsync(); } catch (Exception ex) { error = ex.Message; } finally { isLoading = false; } } }

State Management

// SHARED STATE SERVICE
public class CartService
{
    private List items = new();
    public event Action OnChange;

    public List Items => items;

    public void AddItem(Product product)
    {
        var existingItem = items.FirstOrDefault(i => i.ProductId == product.Id);
        if (existingItem != null)
        {
            existingItem.Quantity++;
        }
        else
        {
            items.Add(new CartItem
            {
                ProductId = product.Id,
                ProductName = product.Name,
                Price = product.Price,
                Quantity = 1
            });
        }
        NotifyStateChanged();
    }

    public void RemoveItem(int productId)
    {
        var item = items.FirstOrDefault(i => i.ProductId == productId);
        if (item != null)
        {
            items.Remove(item);
            NotifyStateChanged();
        }
    }

    public decimal GetTotal()
    {
        return items.Sum(i => i.Price * i.Quantity);
    }

    private void NotifyStateChanged() => OnChange?.Invoke();
}

// REGISTER AS SCOPED
builder.Services.AddScoped();

---

// USE IN COMPONENT
@inject CartService CartService
@implements IDisposable

Shopping Cart (@CartService.Items.Count items)

Total: @CartService.GetTotal().ToString("C")

@code { protected override void OnInitialized() { CartService.OnChange += StateHasChanged; } public void Dispose() { CartService.OnChange -= StateHasChanged; } }

Blazor vs React: When to Choose What?

CHOOSE BLAZOR WHEN:
───────────────────
✅ Team knows C# but not JavaScript
✅ Building internal enterprise apps
✅ Want to share code between client/server
✅ Need strong typing and compile-time safety
✅ Working within .NET ecosystem
✅ Blazor Server is acceptable (internal apps)
✅ Building Progressive Web Apps with offline support

CHOOSE REACT WHEN:
──────────────────
✅ Team knows JavaScript/TypeScript well
✅ SEO is critical (better SSR story with Next.js)
✅ Need largest ecosystem of components/libraries
✅ Building public-facing consumer apps
✅ Mobile app needed (React Native)
✅ Maximum animation/interaction performance required
✅ Need flexibility of JavaScript ecosystem

REALITY CHECK:
──────────────
Both are excellent frameworks. Choice often comes down to:
- Team skills (C# vs JavaScript)
- Project requirements (internal vs public)
- Ecosystem needs (.NET vs Node.js)

Many companies use BOTH:
- Blazor for internal admin panels
- React for customer-facing apps

Best Practices for Blazor

  • Keep components small: One component, one responsibility
  • Use async/await: All I/O operations should be async
  • Implement IDisposable: Clean up event subscriptions and timers
  • Use proper lifecycle methods: OnInitializedAsync for data loading
  • State management: Use services for shared state across components
  • Optimize rendering: Use @key for lists, ShouldRender for optimization
  • Handle loading states: Show spinners, don't leave users guessing
  • Error handling: Use try-catch and display friendly error messages

Master Blazor with Expert Mentorship

Our Full Stack .NET program covers both Blazor and React, giving you flexibility to choose the right tool for each project. Build real applications with personalized guidance.

Explore Full Stack .NET Program

Related Articles