Nextended.Blazor
Blazor-specific helpers, extensions, and utilities for building modern web applications.
Overview
Nextended.Blazor provides a collection of utilities and extensions specifically designed for Blazor applications, including component helpers, JavaScript interop utilities, and navigation extensions.
Installation
dotnet add package Nextended.Blazor
Key Features
1. JavaScript Interop Helpers
Simplified JavaScript interop for common operations.
@inject IJSRuntime JSRuntime
@using Nextended.Blazor.Extensions
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Call JavaScript function
await JSRuntime.InvokeVoidAsync("console.log", "Component initialized");
// Get value from JavaScript
var windowWidth = await JSRuntime.InvokeAsync<int>("eval", "window.innerWidth");
}
}
}
2. Component Helpers
Utilities for working with Blazor components.
@using Nextended.Blazor.Helper
@code {
private ElementReference myElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Focus element
await ComponentHelper.FocusAsync(myElement);
}
}
}
3. Navigation Extensions
Enhanced navigation utilities for Blazor applications.
@inject NavigationManager Navigation
@using Nextended.Blazor.Extensions
@code {
private void NavigateToProfile()
{
// Navigate with parameters
Navigation.NavigateTo($"/profile/{userId}");
// Navigate with force reload
Navigation.NavigateTo("/home", forceLoad: true);
}
private string GetCurrentPage()
{
return Navigation.ToBaseRelativePath(Navigation.Uri);
}
}
4. Localization Support
Simplified localization helpers for Blazor components.
@inject IStringLocalizer<Resources> Localizer
@using Nextended.Blazor.Extensions
<h1>@Localizer["Welcome"]</h1>
<p>@Localizer["Description"]</p>
@code {
private string GetLocalizedMessage(string key)
{
return Localizer[key];
}
}
Usage Examples
Custom Component with Extensions
@using Nextended.Blazor.Extensions
@using Nextended.Core.Extensions
@inject IJSRuntime JSRuntime
<div @ref="containerRef" class="user-card">
<h3>@User.Name.ToPascalCase()</h3>
<p>@User.Email</p>
<button @onclick="CopyEmail">Copy Email</button>
</div>
@code {
[Parameter]
public User User { get; set; }
private ElementReference containerRef;
private async Task CopyEmail()
{
await JSRuntime.InvokeVoidAsync(
"navigator.clipboard.writeText",
User.Email
);
// Show notification
await JSRuntime.InvokeVoidAsync(
"alert",
"Email copied to clipboard!"
);
}
}
Data Loading Component
@using Nextended.Blazor.Models
@using Nextended.Core.Extensions
@typeparam TItem
<div class="data-container">
@if (IsLoading)
{
<div class="spinner">Loading...</div>
}
else if (HasError)
{
<div class="error">@ErrorMessage</div>
<button @onclick="Reload">Retry</button>
}
else if (Items?.Any() == true)
{
@ChildContent(Items)
}
else
{
<div class="no-data">No items found</div>
}
</div>
@code {
[Parameter]
public Func<Task<List<TItem>>> LoadData { get; set; }
[Parameter]
public RenderFragment<List<TItem>> ChildContent { get; set; }
private List<TItem> Items { get; set; }
private bool IsLoading { get; set; }
private bool HasError { get; set; }
private string ErrorMessage { get; set; }
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
}
private async Task LoadDataAsync()
{
IsLoading = true;
HasError = false;
StateHasChanged();
try
{
Items = await LoadData();
}
catch (Exception ex)
{
HasError = true;
ErrorMessage = ex.Message;
}
finally
{
IsLoading = false;
StateHasChanged();
}
}
private async Task Reload()
{
await LoadDataAsync();
}
}
Form Component with Validation
@using Microsoft.AspNetCore.Components.Forms
@using Nextended.Core.Extensions
<EditForm Model="@Model" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<label>Name:</label>
<InputText @bind-Value="Model.Name" class="form-control" />
<ValidationMessage For="@(() => Model.Name)" />
</div>
<div class="form-group">
<label>Email:</label>
<InputText @bind-Value="Model.Email" class="form-control" />
<ValidationMessage For="@(() => Model.Email)" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</EditForm>
@code {
[Parameter]
public UserModel Model { get; set; }
[Parameter]
public EventCallback<UserModel> OnSubmit { get; set; }
private async Task HandleValidSubmit()
{
await OnSubmit.InvokeAsync(Model);
}
}
Interactive Data Table
@using Nextended.Core.Extensions
@typeparam TItem
<div class="data-table">
<div class="table-header">
<input type="text"
@bind="searchTerm"
@bind:event="oninput"
placeholder="Search..."
class="search-box" />
</div>
<table class="table">
<thead>
<tr>
@HeaderContent
</tr>
</thead>
<tbody>
@if (FilteredItems?.Any() == true)
{
foreach (var item in FilteredItems)
{
<tr>
@RowContent(item)
</tr>
}
}
else
{
<tr>
<td colspan="100">No items found</td>
</tr>
}
</tbody>
</table>
<div class="table-footer">
<span>Showing @FilteredItems.Count of @Items.Count items</span>
</div>
</div>
@code {
[Parameter]
public List<TItem> Items { get; set; } = new();
[Parameter]
public RenderFragment HeaderContent { get; set; }
[Parameter]
public RenderFragment<TItem> RowContent { get; set; }
[Parameter]
public Func<TItem, string, bool> FilterFunction { get; set; }
private string searchTerm = string.Empty;
private List<TItem> FilteredItems =>
string.IsNullOrWhiteSpace(searchTerm)
? Items
: Items.Where(item => FilterFunction?.Invoke(item, searchTerm) ?? true).ToList();
}
Best Practices
1. Use StateHasChanged Wisely
protected override async Task OnInitializedAsync()
{
// Load data
data = await LoadDataAsync();
// Trigger re-render
StateHasChanged();
}
2. Implement Proper Disposal
@implements IDisposable
@code {
private Timer timer;
protected override void OnInitialized()
{
timer = new Timer(async _ => await UpdateData(), null, 0, 5000);
}
public void Dispose()
{
timer?.Dispose();
}
}
3. Use Cascading Parameters for Shared State
<CascadingValue Value="@currentUser">
@ChildContent
</CascadingValue>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
private User currentUser;
}
4. Optimize Rendering with ShouldRender
private int updateCount = 0;
protected override bool ShouldRender()
{
updateCount++;
// Only render every 5th update
return updateCount % 5 == 0;
}
Configuration
Program.cs Setup
using Nextended.Blazor.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add Blazor services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// Add Nextended.Blazor services (if any)
// builder.Services.AddNextendedBlazor();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
Supported Frameworks
- .NET 8.0
- .NET 9.0
Dependencies
Nextended.Core- Core utilities and extensionsMicrosoft.AspNetCore.Components.Web- Blazor web componentsMicrosoft.Extensions.Localization.Abstractions- Localization support
Related Projects
- Nextended.Core - Foundation library with extensions
- Nextended.Web - ASP.NET Core web utilities