We have a Blazor Server application (.NET 10, Telerik UI for Blazor 13.0) where we use TelerikComboBox with OnRead, Virtual Scrolling, and ValueMapper. The selected value text intermittently fails to display. The ComboBox appears empty even though a value is selected.
The issue is more frequent on staging/production environments (higher SignalR latency), but it also happens on localhost — rarely, and typically on complex pages with heavy rendering (many components, multiple ComboBoxes, grids, etc.).
Component setup (simplified):
<div class="tooltip-target" title="@_selectedDescrizione">
<TelerikComboBox OnRead="@LoadData"
TItem="MyEntity"
TValue="Guid?"
Value="@Value"
ValueChanged="@OnComboValueChanged"
ValueExpression="@ValueExpression"
TextField="@nameof(MyEntity.Nome)"
ValueField="@nameof(MyEntity.Id)"
Filterable="true"
FilterOperator="@StringFilterOperator.Contains"
PageSize="20"
ScrollMode="DropDownScrollMode.Virtual"
ItemHeight="40"
ValueMapper="@GetModelFromValue">
<ComboBoxSettings>
<ComboBoxPopupSettings Height="auto" MaxHeight="40vh" />
</ComboBoxSettings>
<ItemTemplate>
@context.Nome
</ItemTemplate>
</TelerikComboBox>
</div>
OnRead — lazy loading with server-side filtering:
Standard OnRead handler that queries the database with the user's filter text and returns paginated results via ToDataSourceResultAsync:
private async Task LoadData(ComboBoxReadEventArgs args)
{
var filterDescriptor = args.Request.Filters?.FirstOrDefault() as FilterDescriptor;
string searchText = filterDescriptor?.Value?.ToString() ?? string.Empty;
using var context = await ContextFactory.CreateDbContextAsync();
IQueryable<MyEntity> query = context.Set<MyEntity>().AsNoTracking();
if (!string.IsNullOrWhiteSpace(searchText))
{
var search = searchText.ToLower();
query = query.Where(e => e.Nome.ToLower().Contains(search));
}
var result = await query
.OrderBy(e => e.Nome)
.ToDataSourceResultAsync(args.Request);
args.Data = result.Data;
args.Total = result.Total;
}
ValueMapper — resolves the selected value by ID:
Since we use OnRead with Virtual Scrolling, the pre-selected value may not be in the current page of data. The ValueMapper fetches the entity by ID so the ComboBox can display it. We also store the description as a side-effect for the wrapping tooltip:
private async Task<MyEntity> GetModelFromValue(Guid? selectedValue)
{
if (!selectedValue.HasValue || selectedValue.Value == Guid.Empty)
return null;
return await ExecuteWithStateRefresh(async () =>
{
using var context = await ContextFactory.CreateDbContextAsync();
var item = await context.Set<MyEntity>()
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == selectedValue.Value);
_selectedDescrizione = item?.Descrizione;
return item;
});
}
ExecuteWithStateRefresh — our workaround attempt:
We noticed that after the ValueMapper callback resolves asynchronously (invoked via JS Interop), Telerik does not trigger a re-render on its own. Without intervention, the resolved value frequently doesn't appear. We implemented a two-phase re-render mechanism in our base component class:
private bool _stateRefreshPending;
protected async Task<TResult> ExecuteWithStateRefresh<TResult>(Func<Task<TResult>> func)
{
var result = await func();
_stateRefreshPending = true;
StateHasChanged(); // Batch 1: triggers render + OnAfterRenderAsync
return result;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (_stateRefreshPending)
{
_stateRefreshPending = false;
StateHasChanged(); // Batch 2: deferred render, after client acknowledged Batch 1
}
}
The idea is that StateHasChanged() in Batch 1 travels to the client together with Telerik's JS Interop updates. After the client processes and acknowledges Batch 1, OnAfterRenderAsync fires and triggers Batch 2 — which should arrive after Telerik JS has finished updating the ComboBox internally. This improved reliability significantly, but did not fully solve the issue — the display text still occasionally fails to appear, especially on heavy pages or with network latency.
What we observe:
- The ValueMapper callback executes and returns the correct entity — confirmed by the _selectedDescrizione field being populated correctly (the wrapping <div> tooltip shows the correct description).
- Despite this, the ComboBox itself does not render the display text — it appears empty or shows the placeholder.
- Interacting with the ComboBox (e.g., opening the dropdown, clicking elsewhere) sometimes causes the value to suddenly appear.
- More frequent on staging (network latency), but also reproducible on localhost on complex/heavy pages.
Questions:
1. After the ValueMapper callback returns the resolved object, does the ComboBox process it synchronously or asynchronously on the JS side?
2. Is StateHasChanged() the intended mechanism to force the ComboBox to reflect the ValueMapper result, or is there a different recommended approach?
Environment:
- Telerik UI for Blazor 13.0
- .NET 10, Blazor Server
The issue is more frequent on staging/production environments (higher SignalR latency), but it also happens on localhost — rarely, and typically on complex pages with heavy rendering (many components, multiple ComboBoxes, grids, etc.).
Component setup (simplified):
<div class="tooltip-target" title="@_selectedDescrizione">
<TelerikComboBox OnRead="@LoadData"
TItem="MyEntity"
TValue="Guid?"
Value="@Value"
ValueChanged="@OnComboValueChanged"
ValueExpression="@ValueExpression"
TextField="@nameof(MyEntity.Nome)"
ValueField="@nameof(MyEntity.Id)"
Filterable="true"
FilterOperator="@StringFilterOperator.Contains"
PageSize="20"
ScrollMode="DropDownScrollMode.Virtual"
ItemHeight="40"
ValueMapper="@GetModelFromValue">
<ComboBoxSettings>
<ComboBoxPopupSettings Height="auto" MaxHeight="40vh" />
</ComboBoxSettings>
<ItemTemplate>
@context.Nome
</ItemTemplate>
</TelerikComboBox>
</div>
OnRead — lazy loading with server-side filtering:
Standard OnRead handler that queries the database with the user's filter text and returns paginated results via ToDataSourceResultAsync:
private async Task LoadData(ComboBoxReadEventArgs args)
{
var filterDescriptor = args.Request.Filters?.FirstOrDefault() as FilterDescriptor;
string searchText = filterDescriptor?.Value?.ToString() ?? string.Empty;
using var context = await ContextFactory.CreateDbContextAsync();
IQueryable<MyEntity> query = context.Set<MyEntity>().AsNoTracking();
if (!string.IsNullOrWhiteSpace(searchText))
{
var search = searchText.ToLower();
query = query.Where(e => e.Nome.ToLower().Contains(search));
}
var result = await query
.OrderBy(e => e.Nome)
.ToDataSourceResultAsync(args.Request);
args.Data = result.Data;
args.Total = result.Total;
}
ValueMapper — resolves the selected value by ID:
Since we use OnRead with Virtual Scrolling, the pre-selected value may not be in the current page of data. The ValueMapper fetches the entity by ID so the ComboBox can display it. We also store the description as a side-effect for the wrapping tooltip:
private async Task<MyEntity> GetModelFromValue(Guid? selectedValue)
{
if (!selectedValue.HasValue || selectedValue.Value == Guid.Empty)
return null;
return await ExecuteWithStateRefresh(async () =>
{
using var context = await ContextFactory.CreateDbContextAsync();
var item = await context.Set<MyEntity>()
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == selectedValue.Value);
_selectedDescrizione = item?.Descrizione;
return item;
});
}
ExecuteWithStateRefresh — our workaround attempt:
We noticed that after the ValueMapper callback resolves asynchronously (invoked via JS Interop), Telerik does not trigger a re-render on its own. Without intervention, the resolved value frequently doesn't appear. We implemented a two-phase re-render mechanism in our base component class:
private bool _stateRefreshPending;
protected async Task<TResult> ExecuteWithStateRefresh<TResult>(Func<Task<TResult>> func)
{
var result = await func();
_stateRefreshPending = true;
StateHasChanged(); // Batch 1: triggers render + OnAfterRenderAsync
return result;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (_stateRefreshPending)
{
_stateRefreshPending = false;
StateHasChanged(); // Batch 2: deferred render, after client acknowledged Batch 1
}
}
The idea is that StateHasChanged() in Batch 1 travels to the client together with Telerik's JS Interop updates. After the client processes and acknowledges Batch 1, OnAfterRenderAsync fires and triggers Batch 2 — which should arrive after Telerik JS has finished updating the ComboBox internally. This improved reliability significantly, but did not fully solve the issue — the display text still occasionally fails to appear, especially on heavy pages or with network latency.
What we observe:
- The ValueMapper callback executes and returns the correct entity — confirmed by the _selectedDescrizione field being populated correctly (the wrapping <div> tooltip shows the correct description).
- Despite this, the ComboBox itself does not render the display text — it appears empty or shows the placeholder.
- Interacting with the ComboBox (e.g., opening the dropdown, clicking elsewhere) sometimes causes the value to suddenly appear.
- More frequent on staging (network latency), but also reproducible on localhost on complex/heavy pages.
Questions:
1. After the ValueMapper callback returns the resolved object, does the ComboBox process it synchronously or asynchronously on the JS side?
2. Is StateHasChanged() the intended mechanism to force the ComboBox to reflect the ValueMapper result, or is there a different recommended approach?
Environment:
- Telerik UI for Blazor 13.0
- .NET 10, Blazor Server

I am seeing the same behavior with the same environment setup.
My workaround was to add a 100 ms task delay in the value mapper logic, which makes me think a race condition is occuring.