Home C# / .NET Mastering Blazor Component Lifecycle: A Deep Dive for Advanced Developers
Advanced 3 min · July 13, 2026

Mastering Blazor Component Lifecycle: A Deep Dive for Advanced Developers

Explore Blazor component lifecycle methods with production-ready C# examples.

N
Naren Founder & Principal Engineer

20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 13, 2026
last updated
2,043
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic knowledge of C# and ASP.NET Core
  • Familiarity with Blazor components and Razor syntax
  • Understanding of async/await patterns
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Blazor components have a well-defined lifecycle with methods like OnInitialized, OnParametersSet, OnAfterRender, and Dispose.
  • Lifecycle methods run in a specific order: first initialization, then parameter changes, then rendering, and finally disposal.
  • StateHasChanged triggers re-rendering; understanding when to call it prevents performance issues.
  • Async versions (OnInitializedAsync, OnParametersSetAsync) allow await operations during lifecycle.
  • Proper disposal of resources via IDisposable or IAsyncDisposable is critical to avoid memory leaks.
✦ Definition~90s read
What is Blazor Component Lifecycle?

Blazor component lifecycle is the sequence of methods that a component goes through from creation to disposal, allowing you to hook into key events like initialization, parameter changes, rendering, and cleanup.

Think of a Blazor component like a stage actor.
Plain-English First

Think of a Blazor component like a stage actor. The actor goes through a script: first they get their costume and props (initialization), then they react to audience cues (parameter changes), perform their scene (render), and finally take a bow and leave (dispose). Each step has a specific time and order, and if you miss a cue, the performance suffers.

Blazor components are the building blocks of modern web UIs in ASP.NET Core. They offer a component-based architecture similar to React or Angular, but with C# instead of JavaScript. Understanding the component lifecycle is crucial for building responsive, efficient, and bug-free applications. Whether you're fetching data from an API, handling user interactions, or managing resources, knowing when and how to hook into lifecycle events can make or break your app's performance and reliability.

Consider a real-world scenario: you're building a dashboard that loads data from a database. If you fetch data in the wrong lifecycle method, you might cause unnecessary re-renders, duplicate network calls, or even memory leaks. This tutorial will guide you through each lifecycle method with production-ready examples, common pitfalls, and debugging strategies. By the end, you'll be able to write Blazor components that are both efficient and maintainable.

1. Overview of Blazor Component Lifecycle

Blazor components go through a predictable lifecycle from creation to disposal. The key methods are:

  • SetParametersAsync: Called when the parent component sets parameters.
  • OnInitialized / OnInitializedAsync: Called once when the component is initialized.
  • OnParametersSet / OnParametersSetAsync: Called after parameters are set, including on first render.
  • OnAfterRender / OnAfterRenderAsync: Called after each render of the component.
  • ShouldRender: Determines whether the component should render.
  • Dispose: Called when the component is removed from the UI.

Understanding the order and purpose of these methods is essential. For example, OnInitializedAsync is ideal for one-time async operations like fetching data, while OnParametersSetAsync is better for reacting to parameter changes. The following sections will dive into each method with code examples.

LifecycleOrder.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@page "/lifecycle-order"

<h3>Lifecycle Order Demo</h3>
<p>@message</p>

@code {
    private string message = "";
    private int renderCount = 0;

    protected override void OnInitialized()
    {
        message += "OnInitialized called. ";
    }

    protected override async Task OnInitializedAsync()
    {
        await Task.Delay(1);
        message += "OnInitializedAsync called. ";
    }

    protected override void OnParametersSet()
    {
        message += "OnParametersSet called. ";
    }

    protected override async Task OnParametersSetAsync()
    {
        await Task.Delay(1);
        message += "OnParametersSetAsync called. ";
    }

    protected override void OnAfterRender(bool firstRender)
    {
        renderCount++;
        message += $"OnAfterRender (firstRender={firstRender}) called. ";
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await Task.Delay(1);
        message += $"OnAfterRenderAsync (firstRender={firstRender}) called. ";
    }

    protected override bool ShouldRender()
    {
        message += "ShouldRender called. ";
        return true;
    }

    public void Dispose()
    {
        message += "Dispose called. ";
    }
}
Output
OnInitialized called. OnInitializedAsync called. OnParametersSet called. OnParametersSetAsync called. ShouldRender called. OnAfterRender (firstRender=True) called. OnAfterRenderAsync (firstRender=True) called.
🔥Order Matters
📊 Production Insight
In production, avoid heavy synchronous work in lifecycle methods as they can block the UI thread. Always prefer async methods for I/O operations.
🎯 Key Takeaway
Blazor lifecycle methods execute in a fixed order: initialization, parameter setting, render, and disposal. Async versions allow await operations but do not block the UI thread.

2. SetParametersAsync: The First Entry Point

SetParametersAsync is called when the parent component sets parameters on the child component. It receives a ParameterView that contains all the parameters. This method is called before OnInitialized and OnParametersSet. You can override it to perform custom parameter validation or transformation.

Common use cases
  • Validate parameter values and throw exceptions if invalid.
  • Transform parameters before they are used.
  • Cancel rendering if parameters haven't changed (by not calling base.SetParametersAsync).

Here's an example where we validate a parameter and prevent unnecessary re-renders:

SetParametersAsyncExample.razorCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@code {
    [Parameter] public int Count { get; set; }
    private int previousCount;

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        // Validate parameter
        if (parameters.TryGetValue<int>(nameof(Count), out var newCount))
        {
            if (newCount < 0)
                throw new ArgumentException("Count must be non-negative.");
        }

        // Only call base if parameters actually changed
        if (newCount != previousCount)
        {
            previousCount = newCount;
            await base.SetParametersAsync(parameters);
        }
        // else skip rendering
    }
}
💡Avoid Unnecessary Renders
📊 Production Insight
Be careful: if you skip base.SetParametersAsync, the component's parameters won't be updated, which might cause inconsistencies. Only skip if you are sure the component doesn't need to react to the parameter change.
🎯 Key Takeaway
Override SetParametersAsync to validate or transform parameters, and to avoid unnecessary re-renders by comparing old and new values.

3. OnInitialized and OnInitializedAsync: One-Time Initialization

OnInitialized and OnInitializedAsync are called once when the component is first created. They are ideal for one-time setup tasks like: - Fetching initial data from a service. - Initializing fields or subscriptions. - Setting up timers.

The async version allows you to await asynchronous operations. During this time, the component will render once with the initial state, and then again after the async operation completes.

Important: These methods are not called again if the component is re-rendered due to parent re-renders. They only run once per component instance.

DataFetching.razorCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@inject IWeatherService WeatherService

<h3>Weather Forecast</h3>
@if (forecasts == null)
{
    <p>Loading...</p>
}
else
{
    <table>
        <thead>
            <tr><th>Date</th><th>Temp (C)</th></tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr><td>@forecast.Date.ToShortDateString()</td><td>@forecast.TemperatureC</td></tr>
            }
        </tbody>
    </table>
}

@code {
    private List<WeatherForecast>? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await WeatherService.GetForecastsAsync();
    }
}
⚠ Avoid Calling StateHasChanged in OnInitializedAsync
📊 Production Insight
If your component can be reused in different pages, consider using OnParametersSetAsync instead, as OnInitializedAsync only runs once. For example, a component that displays user details based on a UserId parameter should fetch data in OnParametersSetAsync to react to parameter changes.
🎯 Key Takeaway
Use OnInitializedAsync for one-time async initialization like data fetching. The component renders twice: once with initial state, then with the result.

4. OnParametersSet and OnParametersSetAsync: Reacting to Parameter Changes

OnParametersSet and OnParametersSetAsync are called every time the component receives new parameters, including the first time. This makes them suitable for: - Re-fetching data when parameters change. - Updating internal state based on parameters. - Performing calculations that depend on parameters.

Unlike OnInitialized, these methods run on every parameter change, so you must be careful to avoid infinite loops or redundant work.

Example: A component that displays a product based on a ProductId parameter:

ProductDisplay.razorCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@inject IProductService ProductService

<h3>@product?.Name</h3>
<p>@product?.Description</p>

@code {
    [Parameter] public int ProductId { get; set; }
    private Product? product;
    private int previousProductId;

    protected override async Task OnParametersSetAsync()
    {
        if (ProductId != previousProductId)
        {
            previousProductId = ProductId;
            product = await ProductService.GetProductAsync(ProductId);
        }
    }
}
💡Compare Parameters to Avoid Unnecessary Work
📊 Production Insight
Be aware that OnParametersSetAsync can be called multiple times in quick succession (e.g., when multiple parameters change). Consider debouncing or using a cancellation token to avoid race conditions.
🎯 Key Takeaway
OnParametersSetAsync is the right place to react to parameter changes. Always guard against unnecessary work by comparing old and new values.

5. OnAfterRender and OnAfterRenderAsync: Interacting with the DOM

OnAfterRender and OnAfterRenderAsync are called after each render of the component. They receive a boolean parameter firstRender that is true only on the first render. These methods are the only place where you can safely interact with the rendered DOM, such as: - Calling JavaScript interop functions that require DOM elements. - Measuring element sizes or positions. - Focusing an input element. - Initializing third-party JavaScript libraries.

Because the DOM is guaranteed to be updated, you can use ElementReference parameters here.

FocusInput.razorCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@inject IJSRuntime JS

<input @ref="myInput" />

@code {
    private ElementReference myInput;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync("focusElement", myInput);
        }
    }
}
🔥firstRender Flag
📊 Production Insight
Avoid calling StateHasChanged inside OnAfterRenderAsync, as it can cause an infinite loop. If you need to update state after a JavaScript call, consider using a flag or a separate method.
🎯 Key Takeaway
OnAfterRenderAsync is the safe place to interact with the DOM via JavaScript interop. Use the firstRender flag for one-time initialization.

6. ShouldRender: Controlling Render Behavior

ShouldRender is called before each render and returns a boolean. If it returns false, the component will not render, and OnAfterRender will not be called. This is useful for performance optimization when you know the component's output hasn't changed.

Common use cases
  • Suppress rendering when parameters haven't changed (though this is better handled in SetParametersAsync).
  • Suppress rendering during rapid updates (e.g., while dragging).
  • Implement custom change detection.

Example: A component that only renders when a specific condition is met:

ConditionalRender.razorCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@code {
    [Parameter] public bool Visible { get; set; }
    private bool previousVisible;

    protected override bool ShouldRender()
    {
        // Only render if Visible changed
        if (Visible != previousVisible)
        {
            previousVisible = Visible;
            return true;
        }
        return false;
    }
}
⚠ ShouldRender vs SetParametersAsync
📊 Production Insight
Overusing ShouldRender can lead to stale UI. Always ensure that the component re-renders when its output actually changes. Test thoroughly.
🎯 Key Takeaway
Override ShouldRender to suppress unnecessary renders. Use it judiciously to avoid unexpected UI behavior.

7. Dispose: Cleaning Up Resources

When a component is removed from the UI, Blazor calls Dispose if the component implements IDisposable or IAsyncDisposable. This is crucial for releasing unmanaged resources like: - Event handlers or subscriptions. - Timers. - Network streams. - JavaScript interop handles.

Failing to dispose can cause memory leaks and unexpected behavior.

TimerComponent.razorCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@implements IDisposable

<h3>Timer: @currentTime</h3>

@code {
    private System.Threading.Timer? timer;
    private string currentTime = DateTime.Now.ToLongTimeString();

    protected override void OnInitialized()
    {
        timer = new System.Threading.Timer(_ =>
        {
            InvokeAsync(() =>
            {
                currentTime = DateTime.Now.ToLongTimeString();
                StateHasChanged();
            });
        }, null, 0, 1000);
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}
💡Use InvokeAsync for Thread Safety
📊 Production Insight
For async disposal, implement IAsyncDisposable. This is useful for disposing async resources like database connections. However, be aware that the framework will call both Dispose and DisposeAsync if both are implemented; ensure they don't conflict.
🎯 Key Takeaway
Always implement IDisposable to clean up resources like timers and event subscriptions. Use InvokeAsync for UI updates from background threads.

8. Advanced Patterns: StateHasChanged and Render Trees

StateHasChanged is the method that tells Blazor to re-render the component. It's automatically called after lifecycle events, but you can call it manually to trigger a render. However, overusing it can lead to performance issues.

Best practices
  • Call StateHasChanged only when the component's state has actually changed.
  • Avoid calling it inside lifecycle methods that are already followed by a render (like OnInitializedAsync).
  • Use ShouldRender to suppress unnecessary renders.

Another advanced concept is the render tree. Blazor builds a render tree from your Razor markup and diffs it with the previous tree to update the DOM efficiently. Understanding this can help you optimize complex UIs.

ManualStateHasChanged.razorCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@page "/counter"

<h3>Counter</h3>
<p>Current count: @currentCount</p>
<button @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        // StateHasChanged is automatically called after event handlers
    }

    private async Task UpdateFromExternal()
    {
        // Simulate external update
        await Task.Delay(1000);
        currentCount = 100;
        StateHasChanged(); // Manual call needed
    }
}
🔥Automatic StateHasChanged
📊 Production Insight
In large component trees, frequent StateHasChanged calls can degrade performance. Consider using ShouldRender or implementing IComponent to have fine-grained control over rendering.
🎯 Key Takeaway
StateHasChanged triggers a re-render. Rely on automatic calls in event handlers and lifecycle methods; call manually only when necessary.
● Production incidentPOST-MORTEMseverity: high

The Infinite Loop That Brought Down a Dashboard

Symptom
Users reported that the dashboard page became unresponsive and the browser tab crashed after a few seconds.
Assumption
The developer assumed that calling StateHasChanged in OnParametersSetAsync would only update the UI once.
Root cause
The component was re-rendering in an infinite loop because StateHasChanged triggered a new render cycle, which called OnParametersSetAsync again, leading to a stack overflow.
Fix
Moved the data loading logic to OnInitializedAsync and used a flag to prevent re-fetching on subsequent renders.
Key lesson
  • Avoid calling StateHasChanged inside OnParametersSetAsync unless absolutely necessary.
  • Use OnInitializedAsync for one-time initialization tasks.
  • Implement IDisposable to clean up timers or subscriptions that could cause re-renders.
  • Always test with simulated parameter changes to catch infinite loops.
Production debug guideSymptom to Action4 entries
Symptom · 01
Component re-renders unexpectedly
Fix
Check if StateHasChanged is called unnecessarily. Use logging in lifecycle methods to trace render count.
Symptom · 02
Data not loaded on first render
Fix
Ensure data fetching is in OnInitializedAsync, not in OnParametersSetAsync if it's a one-time load.
Symptom · 03
Memory leak after navigating away
Fix
Verify that IDisposable is implemented and all subscriptions/timers are disposed.
Symptom · 04
UI flickers during parameter updates
Fix
Consider using ShouldRender to suppress unnecessary renders, or batch updates.
★ Quick Debug Cheat Sheet for Blazor LifecycleCommon symptoms and immediate actions for Blazor lifecycle issues.
Infinite re-render loop
Immediate action
Add a breakpoint in OnParametersSetAsync and check call stack.
Commands
Console.WriteLine($"Render count: {renderCount}");
Check if StateHasChanged is called inside OnParametersSetAsync.
Fix now
Move data loading to OnInitializedAsync and use a flag.
Data not refreshed after parameter change+
Immediate action
Log parameter values in OnParametersSetAsync.
Commands
Console.WriteLine($"Parameter changed: {Param}");
Ensure SetParametersAsync is called.
Fix now
Override OnParametersSetAsync to re-fetch data based on parameter changes.
Memory leak+
Immediate action
Check for event subscriptions or timers not disposed.
Commands
Check if component implements IDisposable.
Use dotMemory or memory profiler.
Fix now
Implement IDisposable and unsubscribe/dispose in Dispose method.
MethodWhen CalledUse Case
SetParametersAsyncWhen parent sets parametersParameter validation, prevent re-render
OnInitializedAsyncOnce on first renderOne-time data fetch
OnParametersSetAsyncEvery time parameters setReact to parameter changes
OnAfterRenderAsyncAfter each renderJavaScript interop, DOM access
ShouldRenderBefore each renderConditional rendering
DisposeWhen component removedCleanup resources
⚙ Quick Reference
8 commands from this guide
FileCommand / CodePurpose
LifecycleOrder.cs@page "/lifecycle-order"1. Overview of Blazor Component Lifecycle
SetParametersAsyncExample.razor@code {2. SetParametersAsync
DataFetching.razor@inject IWeatherService WeatherService3. OnInitialized and OnInitializedAsync
ProductDisplay.razor@inject IProductService ProductService4. OnParametersSet and OnParametersSetAsync
FocusInput.razor@inject IJSRuntime JS5. OnAfterRender and OnAfterRenderAsync
ConditionalRender.razor@code {6. ShouldRender
TimerComponent.razor@implements IDisposable7. Dispose
ManualStateHasChanged.razor@page "/counter"8. Advanced Patterns

Key takeaways

1
Blazor lifecycle methods follow a strict order
initialization, parameter setting, render, and disposal.
2
Use OnInitializedAsync for one-time async setup and OnParametersSetAsync for reacting to parameter changes.
3
Always implement IDisposable to clean up resources and avoid memory leaks.
4
Avoid calling StateHasChanged inside lifecycle methods that already trigger a render.
5
Override ShouldRender or SetParametersAsync to optimize rendering performance.

Common mistakes to avoid

3 patterns
×

Calling StateHasChanged inside OnInitializedAsync

×

Not implementing IDisposable for timers or event subscriptions

×

Fetching data in OnInitializedAsync for components that receive parameters

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Explain the order of lifecycle methods in a Blazor component.
Q02SENIOR
How would you implement a component that fetches data every time a param...
Q03SENIOR
What are the potential pitfalls of calling StateHasChanged inside OnAfte...
Q01 of 03JUNIOR

Explain the order of lifecycle methods in a Blazor component.

ANSWER
The order is: SetParametersAsync, OnInitialized/OnInitializedAsync, OnParametersSet/OnParametersSetAsync, ShouldRender, OnAfterRender/OnAfterRenderAsync, and Dispose. Async versions run after their synchronous counterparts.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between OnInitialized and OnParametersSet?
02
Can I call StateHasChanged inside OnAfterRender?
03
How do I prevent a component from re-rendering when parameters haven't changed?
04
What is the proper way to dispose resources in Blazor?
05
Why is my component not updating after a parameter change?
N
Naren Founder & Principal Engineer

20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 13, 2026
last updated
2,043
articles · all by Naren
🔥

That's ASP.NET. Mark it forged?

3 min read · try the examples if you haven't

Previous
EF Core Advanced Querying Patterns
20 / 35 · ASP.NET
Next
Blazor Forms and Validation