I want to set the active panel index based on a query parameter value
Problem Description
When navigating to a Blazor page containing MudTabs with a query parameter specifying the active tab index (e.g., ?tab=1), the application throws an ArgumentOutOfRangeException during the component's first render cycle.
Exception Details
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index') at System.Collections.Generic.List`1.get_Item(Int32 index) at MudBlazor.MudTabs.OnAfterRenderAsync(Boolean firstRender)Environment
- MudBlazor Version: 7.15.0
- Blazor Hosting Model: Server
- .NET Version: 9.0
Minimal Reproducible Example
Page Component (.razor)
@page "/example/{Id:long}"@using MudBlazor<MudTabs ActivePanelIndexChanged="@OnActivePanelChanged" @ref="_tabs"><MudTabPanel Text="Details"><p>Details content</p></MudTabPanel><MudTabPanel Text="Tab 2" Disabled="@(!_enableTab2)"><p>Tab 2 content</p></MudTabPanel><MudTabPanel Text="Tab 3" Disabled="@(!_enableTab3)"><p>Tab 3 content</p></MudTabPanel></MudTabs>Code Behind (.razor.cs)
public partial class Example : ComponentBase{ [Parameter] public long Id { get; set; } [Parameter, SupplyParameterFromQuery(Name = "tab")] public int? TabFromQuery { get; set; } private MudBlazor.MudTabs? _tabs; private int? _activePanelIndex; private int? _pendingIndexFromQuery; private bool _tabsInitialized; private bool _enableTab2 = false; private bool _enableTab3 = false; protected override Task OnParametersSetAsync() { // Store query parameter for later use if (TabFromQuery is int i && i >= 0) _pendingIndexFromQuery = i; return Task.CompletedTask; } protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); // Try to activate the tab from query parameter if (_tabs?.Panels.Count > 0 && !_tabsInitialized) { _tabsInitialized = true; if (_pendingIndexFromQuery.HasValue) { var count = _tabs.Panels.Count; var index = Math.Min(_pendingIndexFromQuery.Value, count - 1); _tabs.ActivatePanel(index); _activePanelIndex = index; _pendingIndexFromQuery = null; } else { _activePanelIndex = 0; } } } private void OnActivePanelChanged(int index) { _activePanelIndex = index; }}Steps to Reproduce
- Create a page with MudTabs and dynamic/disabled panels
- Navigate to the page with a query parameter:
/example/123?tab=1 - Exception is thrown in
MudTabs.OnAfterRenderAsync()
Root Cause
The exception occurs during the MudTabs component lifecycle:
- OnParametersSetAsync: Query parameter
tab=1is captured - First Render: MudTabs starts rendering,
Panelscollection is being populated - OnAfterRenderAsync(firstRender: true): MudTabs internally tries to access
Panels[ActivePanelIndex] - Problem: At this moment,
Panelscollection is not yet fully initialized or is empty - Result:
ArgumentOutOfRangeExceptionwhen accessing the index
Expected Behavior
- MudTabs should handle cases where
ActivePanelIndexis set before panels are fully initialized - Should gracefully default to index 0 or wait for panels to be ready
- No exception should occur when navigating with query parameters
Workarounds Attempted
All of these still produce the exception:
❌ Attempt 1: Direct ActivePanelIndex Binding
<MudTabs ActivePanelIndex="@_activePanelIndex" ...>Result: Same exception during first render
❌ Attempt 2: Using ActivatePanel in OnAfterRenderAsync
if (_tabs?.Panels.Count > 0){ _tabs.ActivatePanel(requestedIndex);}Result: Exception still occurs before our code executes
❌ Attempt 3: Delaying with StateHasChanged
protected override async Task OnAfterRenderAsync(bool firstRender){ StateHasChanged(); await Task.Delay(100); _tabs?.ActivatePanel(index);}Result: Exception occurs before delay
❌ Attempt 4: Setting in OnInitializedAsync
protected override async Task OnInitializedAsync(){ if (_pendingIndexFromQuery.HasValue) _tabs?.ActivatePanel(_pendingIndexFromQuery.Value);}Result: _tabs is null, panels don't exist yet