Infinite scroll in Blazor DataGrid
3 Dec 202518 minutes to read
The infinite scrolling feature in the Syncfusion® Blazor DataGrid provides load-on-demand data retrieval to handle large datasets without degrading performance. In default infinite scrolling, the Grid fetches the next block of data when the vertical scrollbar reaches the end of the scroller, creating a seamless browsing experience across extensive data.
In this feature, a block is equivalent to the Grid’s PageSize. If PageSize is not set, the Grid calculates it from the viewport height and row height. To enable infinite scrolling, set EnableInfiniteScrolling to true and define a content Height.
- With this feature, the Grid does not issue a new data request when revisiting a previously loaded page.
- The
Heightproperty must be specified whenEnableInfiniteScrollingis enabled (a fixed container height is required).
@using Syncfusion.Blazor.Grids
<SfGrid DataSource="@TaskData" Height="300" EnableInfiniteScrolling="true">
<GridPageSettings PageSize="50"></GridPageSettings>
<GridColumns>
<GridColumn Field=@nameof(TaskDetails.TaskID) HeaderText="TaskID" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Engineer) HeaderText="Engineer" Width="150"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Designation) HeaderText="Designation" Format="d" Type="ColumnType.Date" TextAlign="TextAlign.Right" Width="130"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Estimation) HeaderText="Estimation" Format="C2" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Status) HeaderText="Status" Width="150"></GridColumn>
</GridColumns>
</SfGrid>
@code{
public List<TaskDetails> TaskData { get; set; }
protected override void OnInitialized()
{
TaskData = TaskDetails.GenerateData(5000);
}
}public class TaskDetails
{
public static List<TaskDetails> GenerateData(int count)
{
var names = new List<string> { "TOM", "Hawk", "Jon", "Chandler", "Monica", "Rachel", "Phoebe", "Gunther", "Ross", "Geller", "Joey", "Bing", "Tribbiani", "Janice", "Bong", "Perk", "Green", "Ken", "Adams" };
var hours = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var designations = new List<string> { "Manager", "Engineer 1", "Engineer 2", "Developer", "Tester" };
var statusValues = new List<string> { "Completed", "Open", "In Progress", "Review", "Testing" };
var random = new Random();
var result = new List<TaskDetails>();
// Generate random data.
for (int i = 0; i < count; i++)
{
result.Add(new TaskDetails
{
TaskID = i + 1,
Engineer = names[random.Next(names.Count)],
Designation = designations[random.Next(designations.Count)],
Estimation = hours[random.Next(hours.Count)],
Status = statusValues[random.Next(statusValues.Count)]
});
}
return result;
}
public int TaskID { get; set; }
public string Engineer { get; set; }
public string Designation { get; set; }
public int Estimation { get; set; }
public string Status { get; set; }
}Number of blocks rendered during initial loading
At initial load, the Grid renders a specified number of data blocks (pages), which equates to the InitialBlocks value multiplied by the page size.
Configure this using InitialBlocks on GridInfiniteScrollSettings. By default, three pages are rendered initially. Afterwards, additional data is buffered and loaded based on page size or the number of rows that fit within the given height.
@using Syncfusion.Blazor.Grids
@using Syncfusion.Blazor.DropDowns
<div style="margin-bottom:5px">
<label style="padding: 30px 2px 0 0">Select InitialBlocks count:</label>
<SfDropDownList TValue="int" TItem="Rows" Placeholder="Select count" Width="220px" DataSource="DropDownData">
<DropDownListFieldSettings Text="Text" Value="Value"></DropDownListFieldSettings>
<DropDownListEvents ValueChange="ValueChanged" TValue="int" TItem="Rows"></DropDownListEvents>
</SfDropDownList>
</div>
<SfGrid @ref="Grid" DataSource="@TaskData" Height="300" EnableInfiniteScrolling="true">
<GridPageSettings PageSize="50"></GridPageSettings>
<GridInfiniteScrollSettings InitialBlocks="@InitialBlockValue"></GridInfiniteScrollSettings>
<GridColumns>
<GridColumn Field=@nameof(TaskDetails.TaskID) HeaderText="TaskID" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Engineer) HeaderText="Engineer" Width="150"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Designation) HeaderText="Designation" Format="d" Type="ColumnType.Date" TextAlign="TextAlign.Right" Width="130"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Estimation) HeaderText="Estimation" Format="C2" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Status) HeaderText="Status" Width="150"></GridColumn>
</GridColumns>
</SfGrid>
@code {
public SfGrid<TaskDetails> Grid { get; set; }
public List<TaskDetails> TaskData { get; set; }
protected override void OnInitialized()
{
TaskData = TaskDetails.GenerateData(1000);
}
public int InitialBlockValue { get; set; }
public class Rows
{
public int Text { get; set; }
public int Value { get; set; }
}
private List<Rows> DropDownData = new List<Rows>
{
new Rows() { Text = 1, Value = 1 },
new Rows() { Text = 2, Value = 2 },
new Rows() { Text = 3, Value = 3 },
new Rows() { Text = 4, Value = 4 },
new Rows() { Text = 5, Value = 5 },
new Rows() { Text = 6, Value = 6 },
new Rows() { Text = 7, Value = 7 },
};
public async Task ValueChanged(ChangeEventArgs<int, Rows> Args)
{
InitialBlockValue = Args.Value;
await Grid.Refresh();
}
}public class TaskDetails
{
public static List<TaskDetails> GenerateData(int count)
{
var names = new List<string> { "TOM", "Hawk", "Jon", "Chandler", "Monica", "Rachel", "Phoebe", "Gunther", "Ross", "Geller", "Joey", "Bing", "Tribbiani", "Janice", "Bong", "Perk", "Green", "Ken", "Adams" };
var hours = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var designations = new List<string> { "Manager", "Engineer 1", "Engineer 2", "Developer", "Tester" };
var statusValues = new List<string> { "Completed", "Open", "In Progress", "Review", "Testing" };
var random = new Random();
var result = new List<TaskDetails>();
// Generate random data.
for (int i = 0; i < count; i++)
{
result.Add(new TaskDetails
{
TaskID = i + 1,
Engineer = names[random.Next(names.Count)],
Designation = designations[random.Next(designations.Count)],
Estimation = hours[random.Next(hours.Count)],
Status = statusValues[random.Next(statusValues.Count)]
});
}
return result;
}
public int TaskID { get; set; }
public string Engineer { get; set; }
public string Designation { get; set; }
public int Estimation { get; set; }
public string Status { get; set; }
}Efficient data caching and DOM management in Grid cache mode
In Syncfusion® Blazor DataGrid cache mode, previously loaded blocks are reused when revisited, reducing repeat data requests. The Grid manages the number of rendered DOM row elements using GridInfiniteScrollSettings.MaximumBlocks. When this limit is reached, the Grid removes an older block of row elements to render new ones.
Enable cache mode by setting EnableCache to true on GridInfiniteScrollSettings.
Configure the maximum cached blocks with MaximumBlocks (default: 3).
@using Syncfusion.Blazor.Grids
@using Syncfusion.Blazor.Buttons
<div style="display:flex; margin-bottom:5px">
<label> Enable or Disable Cache mode:</label>
<SfSwitch ValueChange="Change" TChecked="bool"></SfSwitch>
</div>
<SfGrid @ref="Grid" DataSource="@TaskData" Height="300" EnableVirtualization="true">
<GridPageSettings PageSize="50"></GridPageSettings>
<GridInfiniteScrollSettings EnableCache="@IsEnabled"></GridInfiniteScrollSettings>
<GridColumns>
<GridColumn Field=@nameof(TaskDetails.TaskID) HeaderText="TaskID" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Engineer) HeaderText="Engineer" Width="150"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Designation) HeaderText="Designation" Format="d" Type="ColumnType.Date" TextAlign="TextAlign.Right" Width="130"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Estimation) HeaderText="Estimation" Format="C2" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=@nameof(TaskDetails.Status) HeaderText="Status" Width="150"></GridColumn>
</GridColumns>
</SfGrid>
@code{
public SfGrid<TaskDetails> Grid { get; set; }
public List<TaskDetails> TaskData { get; set; }
protected override void OnInitialized()
{
TaskData = TaskDetails.GenerateData(1000);
}
public bool IsEnabled { get; set; }
private void Change(Syncfusion.Blazor.Buttons.ChangeEventArgs<bool> args)
{
IsEnabled = args.Checked;
Grid.Refresh();
}
}public class TaskDetails
{
public static List<TaskDetails> GenerateData(int count)
{
var names = new List<string> { "TOM", "Hawk", "Jon", "Chandler", "Monica", "Rachel", "Phoebe", "Gunther", "Ross", "Geller", "Joey", "Bing", "Tribbiani", "Janice", "Bong", "Perk", "Green", "Ken", "Adams" };
var hours = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var designations = new List<string> { "Manager", "Engineer 1", "Engineer 2", "Developer", "Tester" };
var statusValues = new List<string> { "Completed", "Open", "In Progress", "Review", "Testing" };
var random = new Random();
var result = new List<TaskDetails>();
// Generate random data.
for (int i = 0; i < count; i++)
{
result.Add(new TaskDetails
{
TaskID = i + 1,
Engineer = names[random.Next(names.Count)],
Designation = designations[random.Next(designations.Count)],
Estimation = hours[random.Next(hours.Count)],
Status = statusValues[random.Next(statusValues.Count)]
});
}
return result;
}
public int TaskID { get; set; }
public string Engineer { get; set; }
public string Designation { get; set; }
public int Estimation { get; set; }
public string Status { get; set; }
}Limitations
- Due to browser element height limitations, the maximum number of records the Grid can load is constrained by browser capabilities.
- A static height must be set for the component or its parent container when using infinite scrolling. Using 100% height works only if both the Grid and its parent have explicit heights.
- The combined height of the initially loaded rows must exceed the viewport height for content to scroll.
- With infinite scrolling, copy-paste and drag-and-drop apply only to items within the current viewport.
- Cell selection is not persisted in cache mode.
- Group records cannot be collapsed in cache mode.
- Lazy load grouping with infinite scrolling does not support cache mode; infinite scrolling applies only to parent-level caption rows in this scenario.
- In normal grouping, infinite scrolling is not supported for child items during expand/collapse; all child items load when caption rows are toggled.
- Aggregates and total group items reflect the current view items; this is expected with on-demand data loading.
- Programmatic selection using SelectRowsAsync and SelectRowAsync is not supported in infinite scrolling.
- Infinite scrolling is not compatible with:
- Batch editing
- Normal editing
- Row template
- Row virtual scrolling
- Detail template
- Hierarchy features
- Autofill
- Limitations of row drag and drop with infinite scrolling:
- In cache mode, the Grid refreshes automatically if the number of content
<tr>elements exceeds the cache limit after the drop action. - With lazy load grouping, the Grid refreshes automatically after row drag and drop.
- For remote data, changes from drag and drop are applied only in the UI and are lost after refresh unless persisted to the server. Use the RowDropped event to commit changes server-side, then refresh the Grid.
- In cache mode, the Grid refreshes automatically if the number of content