Blazor Component Created: 01 Feb 2026 Updated: 01 Feb 2026

Blazor Component Lifecycle Events

Introduction

Blazor components follow a well-defined lifecycle from creation to disposal. Understanding these lifecycle events is crucial for building efficient, performant, and maintainable Blazor applications. In this article, we'll explore each lifecycle event, understand when to use them, and examine practical examples.

What Are Lifecycle Events?

Lifecycle events are methods that Blazor automatically calls at specific points during a component's existence. They provide hooks where you can execute custom code, such as:

  1. Loading data from APIs or databases
  2. Initializing component state
  3. Performing JavaScript interop
  4. Optimizing rendering performance
  5. Cleaning up resources

Most lifecycle events come in two versions: synchronous and asynchronous, allowing you to choose the appropriate version based on your needs.

The Component Lifecycle Flow

When a Blazor component is created and rendered, the lifecycle events execute in the following order:

  1. Constructor (standard C# constructor)
  2. OnInitialized()
  3. OnInitializedAsync()
  4. OnParametersSet()
  5. OnParametersSetAsync()
  6. ShouldRender() (if the component needs to render)
  7. OnAfterRender(firstRender)
  8. OnAfterRenderAsync(firstRender)

When a component's state or parameters change, steps 4-8 repeat.

Lifecycle Events in Detail

1. OnInitialized and OnInitializedAsync

When they run: Once, when the component is first initialized.

Use cases:

  1. Loading initial data
  2. Setting up component state
  3. Initializing services

Key points:

  1. These methods run only once during the component's lifetime
  2. OnInitialized() runs first, then OnInitializedAsync()
  3. The UI has not been rendered yet when these execute
  4. Use the async version for long-running operations like API calls

Example:

protected override void OnInitialized()
{
// Quick initialization
currentDate = DateTime.Now;
base.OnInitialized();
}

protected override async Task OnInitializedAsync()
{
// Long-running operations
products = await ProductService.GetProductsAsync();
await base.OnInitializedAsync();
}

Common mistake: Trying to reload data in OnInitialized() when a parameter changes. Since this method only runs once, parameter changes won't trigger a data reload. Use OnParametersSet() instead.

2. OnParametersSet and OnParametersSetAsync

When they run:

  1. After OnInitialized() methods (during initial load)
  2. Every time a parameter value changes

Use cases:

  1. Reloading data when parameters change
  2. Validating parameter values
  3. Deriving state from parameters

Key points:

  1. These methods run on both initialization and parameter changes
  2. Perfect for components that need to update based on parameter values
  3. If you load data here, it will reload every time parameters change

Example:

[Parameter]
public int UserId { get; set; }

private User? currentUser;

protected override async Task OnParametersSetAsync()
{
// This runs both on initial load AND when UserId changes
currentUser = await UserService.GetUserByIdAsync(UserId);
await base.OnParametersSetAsync();
}

Best practice: If your data loading depends on parameter values, use OnParametersSetAsync() instead of OnInitializedAsync() to ensure data refreshes when parameters change.

3. OnAfterRender and OnAfterRenderAsync

When they run: After the component has finished rendering.

Use cases:

  1. JavaScript interop (required when prerendering is enabled)
  2. DOM manipulation
  3. Setting focus on elements
  4. Initializing JavaScript libraries

Key points:

  1. All HTML elements are rendered and available in the DOM
  2. The firstRender parameter indicates if this is the component's first render
  3. Use firstRender to run code only once after the initial render
  4. This is the only safe place for JavaScript interop when prerendering is enabled

Example:

@inject IJSRuntime JSRuntime
private ElementReference searchInput;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Initialize JavaScript library only on first render
await JSRuntime.InvokeVoidAsync("initializeTooltips");
// Focus the search input
await searchInput.FocusAsync();
}
await base.OnAfterRenderAsync(firstRender);
}

Important: If you disable prerendering, you can call JavaScript in other lifecycle methods, but it's still a best practice to use OnAfterRender methods to ensure the DOM is ready.

4. ShouldRender

When it runs: Before the component re-renders (after state changes).

Use cases:

  1. Performance optimization
  2. Preventing unnecessary re-renders
  3. Conditional rendering logic

Key points:

  1. Return false to skip rendering
  2. The component always renders at least once, even if this returns false
  3. Does not have an async version
  4. Use sparingly and only when you have measured performance issues

Example:

private int currentValue;
private int lastRenderedValue;

protected override bool ShouldRender()
{
// Only re-render if value actually changed
if (currentValue == lastRenderedValue)
{
return false; // Skip render
}
lastRenderedValue = currentValue;
return true;
}

Warning: Overusing ShouldRender() can lead to bugs where the UI doesn't update when it should. Only use it for measured performance optimizations.

Practical Examples

Example 1: Loading Data with Parameters

@page "/product/{ProductId:int}"

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

@code {
[Parameter]
public int ProductId { get; set; }
private Product? product;
private bool isLoading = true;
protected override async Task OnParametersSetAsync()
{
// Runs on initial load AND when ProductId changes
isLoading = true;
product = await ProductService.GetProductAsync(ProductId);
isLoading = false;
}
}

Example 2: JavaScript Interop with Focus Management

@inject IJSRuntime JSRuntime

<input @ref="inputElement" type="text" />

@code {
private ElementReference inputElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await inputElement.FocusAsync();
}
}
}

Example 3: Performance Optimization with ShouldRender

<h3>User Count: @userCount</h3>
<button @onclick="UpdateCount">Update</button>

@code {
private int userCount;
private int previousCount;
protected override bool ShouldRender()
{
// Only render if count changed by 10 or more
if (Math.Abs(userCount - previousCount) < 10)
{
return false;
}
previousCount = userCount;
return true;
}
private void UpdateCount()
{
userCount = GetUserCount();
}
}

Common Patterns and Best Practices

1. Async vs. Sync: Which to Choose?

  1. Use async versions for I/O operations (API calls, database queries, file operations)
  2. Use sync versions for quick, in-memory operations
  3. Don't use async methods if you're not doing async work

2. OnInitialized vs. OnParametersSet

  1. Use OnInitialized() for data that doesn't depend on parameters
  2. Use OnParametersSet() for data that depends on parameter values
  3. Remember: OnParametersSet() runs on every parameter change, so cache data if needed

3. Managing State Updates

protected override async Task OnInitializedAsync()
{
isLoading = true;
StateHasChanged(); // Force UI update to show loading indicator
data = await LoadDataAsync();
isLoading = false;
// StateHasChanged() called automatically after lifecycle methods
}

4. Cancellation Token Support

Always support cancellation in long-running async operations:

protected override async Task OnInitializedAsync()
{
try
{
products = await ProductService.GetProductsAsync(CancellationToken);
}
catch (OperationCanceledException)
{
// Component disposed before operation completed
}
}

Common Mistakes to Avoid

❌ Don't: Call JavaScript in OnInitialized

// WRONG - DOM might not be ready
protected override async Task OnInitializedAsync()
{
await JSRuntime.InvokeVoidAsync("setupComponent");
}

✅ Do: Call JavaScript in OnAfterRender

// CORRECT - DOM is guaranteed to be ready
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("setupComponent");
}
}

❌ Don't: Forget to Check firstRender

// WRONG - This will run on every render, causing performance issues
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await JSRuntime.InvokeVoidAsync("expensiveOperation");
}

✅ Do: Use firstRender Parameter

// CORRECT - Only runs once
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("expensiveOperation");
}
}

❌ Don't: Load Parameter-Dependent Data in OnInitialized

[Parameter]
public int CategoryId { get; set; }

// WRONG - Won't reload when CategoryId changes
protected override async Task OnInitializedAsync()
{
products = await GetProductsByCategoryAsync(CategoryId);
}

✅ Do: Load Parameter-Dependent Data in OnParametersSet

[Parameter]
public int CategoryId { get; set; }

// CORRECT - Reloads when CategoryId changes
protected override async Task OnParametersSetAsync()
{
products = await GetProductsByCategoryAsync(CategoryId);
}

Performance Considerations

1. Minimize Re-renders

  1. Use ShouldRender() judiciously for expensive components
  2. Avoid unnecessary state changes
  3. Use @key directive to help Blazor track component identity

2. Async Operation Management

  1. Always use CancellationToken for async operations
  2. Dispose of resources properly
  3. Avoid sync-over-async patterns

3. Data Loading Strategies

// Strategy 1: Load everything upfront
protected override async Task OnInitializedAsync()
{
users = await UserService.GetAllUsersAsync();
products = await ProductService.GetAllProductsAsync();
}

// Strategy 2: Progressive loading
protected override async Task OnInitializedAsync()
{
// Load critical data first
users = await UserService.GetAllUsersAsync();
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Load non-critical data after render
products = await ProductService.GetAllProductsAsync();
StateHasChanged();
}
}

Testing Lifecycle Events

When unit testing components with lifecycle events:

[Fact]
public void Component_CallsDataService_OnInitialization()
{
// Arrange
var mockService = new Mock<IProductService>();
var ctx = new TestContext();
ctx.Services.AddSingleton(mockService.Object);
// Act
var cut = ctx.RenderComponent<ProductList>();
// Assert
mockService.Verify(s => s.GetProductsAsync(), Times.Once);
}

Conclusion

Understanding Blazor component lifecycle events is essential for building robust, efficient applications. Here are the key takeaways:

  1. OnInitialized/Async - Use for one-time initialization and data loading
  2. OnParametersSet/Async - Use when data depends on component parameters
  3. OnAfterRender/Async - Use for JavaScript interop and DOM manipulation
  4. ShouldRender - Use sparingly for performance optimization


Share this lesson: