Environment
Grid ColumnDraggable is never destroyed
In Components/Common/Grid/Rows/TableHeaderRow.cs, Dispose() calls base.Dispose() first, which sets IsDisposed = true. It then calls DestroyColumnDraggable(), which uses InvokeAsync<object>. That method returns early when IsDisposed is true (BaseComponent.InvokeAsync<T>), so destroyColumnDraggable is never sent to the browser. DestroyColumnReorderable() and DestroyColumnResizable() work because they use InvokeVoidAsync, which has no such check.
As a result, every grid with Reorderable="true" or Groupable="true" leaves "<dataId>-telerik-ColumnDraggable" in window.TelerikBlazor._instances. Its draggable.element (the grid's .k-grid-header) keeps the whole disposed page reachable, so the page is never garbage collected.
Object.entries(TelerikBlazor._instances) .filter(([k, i]) => i && i.element && !i.element.isConnected) .map(([k, i]) => k + ' / ' + i.constructor.name)
Sample project attached.
(optional)
one "...-telerik-ColumnDraggable" entry per reorderable or groupable grid, all pointing at detached elements. Each pageA -> pageB round trip adds more, and the detached DOM is never released.
An empty array is returned. Page A's components were disposed, so their JS instances should have been destroyed.
Workaround
After each navigation, we destroy and remove any registered instance whose element is no longer in the page:
const instances = window.TelerikBlazor && window.TelerikBlazor._instances; for (const key of Object.keys(instances || {})) { const i = instances[key]; if (i && i.element && !i.element.isConnected) { try { i.destroy && i.destroy(); } catch (e) { } delete instances[key]; } }
Suggested Fix