Stop re-rendering content that didn't change during interactions
Spun off from #13737, which tracks how many components each item renders. This issue tracks how often content is rendered: interactions that rebuild content that didn't change. Key interceptor work is in #13855.
The usual cause is that a ChildContent parameter, a non-fixed cascade or a new delegate always counts as changed. When a component re-renders itself (opening, toggling, highlighting), everything under it rebuilds. bUnit's renderer doesn't show most of this, so every number below comes from a real browser.
Status by release
v9.9.0 (released 2026-08-24)
| PR | what |
|---|---|
| #13663 | MudDataGrid: stop re-rendering the grid while typing in the column filter menu |
| #13678 | MudDataGrid: remove a dead field that rendered every header cell twice |
| #13677 | Localizer: stop components re-rendering when their localized text is unchanged |
v9.10.0 (released 2026-09-13)
| PR | what |
|---|---|
| #13706 | MudElement: capture element references without an extra render |
| #13730 | MudSelect: skip the extra mount render when nothing resolves |
| #13732, fixed by #13791 | MudTreeView: only re-render items whose state changed |
| #13734 | MudChipSet: only re-render chips whose selection changed |
| #13736, fixed by #13790 | MudTable: only re-render rows whose selection changed |
| #13742, reverted by #13744 | MudRadioGroup: cascade itself as a fixed value (redone in #13798) |
| #13792, #13796 | MudDataGrid: cell-edit, grouped-row and context-menu re-render overhead (#11860) |
v9.11.0 (current)
Merged:
| PR | what |
|---|---|
| #13798 | MudRadioGroup: cascade itself as a fixed value |
| #13862 | MudDataGrid: opening a column filter menu doesn't re-render the grid. 100 rows: 393 -> 347 ms, 315 -> 52 renders |
| #13863 | MudDataGrid: expanding a group renders only that group. 25 groups: 10-12 -> 3 ms |
| #13864 | MudDataGrid: picking a filter-row value renders the grid once. 100 rows: 49 -> 36 ms |
| #13868 | MudDatePicker: the year list renders markup, once. Open the year view 54 -> 20 ms, pick a year 49 -> 29 ms |
| #13851 | MudSelect: options render once per open instead of on every self-render. Open 100 options 120 -> 53 ms, 1000 options 1196 -> 596 ms. Option content reading state changed without any render stays stale while open until the page renders. |
An audit of every merged performance PR since v9.8.0 found stale content or behaviour regressions in a few of them, fixed by:
| PR | fixes |
|---|---|
| #13873 | MudChipSet: chips ignored set parameter changes that came with a selection change (#13734) |
| #13874 | MudDataGrid: the column filter menu stayed open after Enter or Escape with async filtering (#13663) |
| #13875 | MudTimePicker and MudMenu: the browser context menu showed on .NET 8 and 9 (#13684, #13670) |
| #13876 | MudDataGrid: GroupDefinition.Expanded was stale in group templates (#13863) |
| #13877 | MudTreeView: runtime icon, ripple and expand trigger changes didn't reach items (#13732) |
| #13878, #13879 | MudBreadcrumbs and MudExpansionPanels: older gaps of the same kind |
v9.11.0 performance work is complete. More re-render work waits until v9.13.0 or later.
Built and not merged:
| change | scenario | before | after | why |
|---|---|---|---|---|
| MudLayout and MudDrawerContainer: toggling a drawer doesn't rebuild the layout's content | toggle a drawer in a layout of 200 components | 41 ms | 23 ms | Dropped: content can go stale when a child component toggles the drawer. |
| #13858 MudTabs: panels render only when the tabs' parent renders or the active panel changes | mount 12 tabs with KeepPanelsAlive |
178 ms, 4198 renders | 74 ms, 502 renders | Closed: panel content can go stale when OnPreviewInteraction changes state and the active tab is clicked again. |
| #13867 MudCard: cascade the card as a fixed value | type in a 12-field form inside a card | 12 ms per key | 8 ms per key | Closed: every piece of card state its parts read would need its own named cascade, for about 4 ms per key. |
MudDataGrid and MudTable: make row @key work |
sort 100 rows, MudDataGrid | 127 ms | 55 ms | Held for v9.13.0 or later: row state starts following items, and keys depend on item equality. |
| sort 100 rows, MudTable | 52 ms | 36 ms |
Notes:
- Row keys: each row is its own render-tree region inside MudVirtualize, so
@keynever matches and moved rows are recreated on sort and filter. Repeated items must render without a key, or the page crashes with a duplicate key.
| idea | measured |
|---|---|
| MudMenu: guard the items and fix the cascade | 1-4 ms per interaction at 10 items, 7-14 ms at 50 |
| MudSelect: its three renders while opening | about 6 ms of a 46 ms open, no per-option cost |
| MudSelectItem: skip rendering the hidden shadow copies | 100 fewer renders per multi-select toggle, 0 ms |
| MudAutocomplete: guard the result items | 12 -> 9 ms per arrow key at 10 results; typing is dominated by the debounce |
| MudCollapse: don't re-render the content at the end of the transition | about 10 ms on a nav group of 60 links, ranges overlap |
| MudExpansionPanel: guard the content | no change; UpdateAllAsync re-rendering every panel is the cause |
| MudTabs: switching tabs | 12-13 ms either way |
| MudInput and MudTextField: non-rendering key handlers | at most 1 ms per key on WASM |
| MudFormComponent: skip the render when a field's validation messages didn't change | 16 -> 15 ms per commit in a 20-field EditForm |
v9.x (future)
More re-render work waits until the changes from v9.9.0 through v9.11.0 have proven stable, so the items below and the held row keys will probably land in v9.13.0 or later.
Found by render counts in the browser, not timed yet. Each needs a timing before it's worth a PR.
- MudSwipeArea (and so MudCarousel) and MudColorPicker's spectrum re-render on every pointer move with no button pressed.
- MudDropZone re-renders every item in every zone on drag start and drop (96 renders on drag start in a 64-zone chessboard).
- MudAutocomplete destroys and rebuilds its result list right before closing when a result is clicked.
- MudTimePicker re-renders all 25-37 of its number labels on every pointer down, drag step and click. Date and time pickers re-render on every key press, including keys that change nothing.
- MudSnackbarProvider re-renders every visible snackbar when one is added, even when the new one is queued.
- MudTimeline, MudCarousel and MudRating cascade themselves with
IsFixed="false", so every render re-renders every item. - Docs site: every page navigation re-renders the whole left nav, about 290 components on Blazor Server.
- MudExpansionPanels re-renders every panel when one toggles (
UpdateAllAsync), 2N+5 component renders for N panels. - MudStepper re-renders every step title on each step change.
v10 (next breaking release)
- Keep popover content mounted while it is closed. Today closing a MudSelect or MudMenu destroys its content and every open rebuilds it. Keeping it would make reopening nearly free, but closed popups would hold their DOM, which changes the markup. Not measured yet.
- MudSelect without shadow items, tracked in #13737, also removes the renders of the hidden copies and the per-open shadow item rebuild #13851 needs.
Large option lists
A 1000-option MudSelect is not a design target. Past a few hundred options MudAutocomplete is the right component, as the Select docs say. <Virtualize> inside MudSelect breaks keyboard navigation and check state (#12998, #13561).
A probe build counted renders per component type in the browser. Timings are headless Chrome against the Debug WASM test viewer, median of 12 runs over 2 rounds, alternating builds. A ceiling build (the smallest change that removes the extra renders) was compared against the base. Proposed fixes get a shippable version, tests and a browser behaviour A/B against dev before a PR.
Source: MudBlazor/MudBlazor