What is Virtualization?
Virtualization is a performance optimization technique for displaying large data sets by rendering only the visible portion within the viewport (the UI area that users can see). For instance, if you have a million records but your browser window can show only 10 at a time, virtualization ensures only those 10 are initially loaded, with additional records dynamically loading as you scroll.
This approach reduces memory usage and improves application responsiveness, making it especially beneficial for long lists. Virtualization is particularly effective for applications that need to display thousands of items, such as data grids, long lists, or complex dashboards.
How Virtualization Works?
In a virtualized UI, only a small subset of currently visible items (with a few buffer items above and below the viewport for smoother scrolling) are rendered on the screen. As the user scrolls, the component dynamically loads and renders new items while removing those that scroll out of view. This allows the UI to maintain consistent performance, regardless of the total number of items.
Lazy Loading: Virtualized components can fetch or load data incrementally as the user scrolls, reducing network load and allowing data to be loaded on demand.
Virtualization in Blazor
In Blazor, virtualization is implemented using the Virtualize
component. Before using virtualization, here are some key requirements to keep in mind:
- Predictable Item Size Requirement: Virtualization relies on a known
ItemSize
for smooth operation.
- If each item is about the same size, Blazor can easily determine which items are visible and load only those.
- If items vary greatly in size, Blazor may struggle to load the correct items, leading to layout issues or unexpected scrolling behavior.
Example: Without Virtualization
If we render items without virtualization, we might use a foreach
loop like the example below.
<ul>
@foreach(var user in users)
{
<li>
....
</li>
}
</ul>
Code Snippet 1: Component with foreach loop
Here is the full code:
@page "/"
@rendermode InteractiveServer
@inject UserService UserService
<h3>Rendering 10,000 User Profiles using Virtualization</h3>
<p>This will be smooth as browser optimizes the performance.</p>
<ul>
<Virtualize Items="@users" ItemSize="130" Context="user">
<li style="margin: 10px; padding: 20px; border: 1px solid #ccc;">
<div style="display: flex; align-items: center;">
<!-- Conditional styling for FTE employees -->
<div style="@GetImageBorderStyle(user.IsEmployeeFTE)">
<img src="Images/User-Logo.png" alt="User Logo" style="width: 100px; height: 100px;" />
</div>
<div style="margin-left: 20px;">
<h4>@user.Name</h4>
<p style="font-size: 14px;">@user.Description</p>
<label>
<input type="checkbox" @bind="user.IsEmployeeFTE" />
Is FTE
</label>
</div>
</div>
</li>
</Virtualize>
</ul>
@code {
private List<User> users;
protected override void OnInitialized()
{
users = UserService.GetUsers(20000);
}
private string GetImageBorderStyle(bool isFTE)
{
if (isFTE)
{
return "border: 5px solid green; padding: 5px;";
}
else
{
return "border: 5px solid red; padding: 5px;";
}
}
}
Code Snippet 2: Full version of code snippet 1
With this approach, all 10,000 items are loaded at once, as you can cross verify in following output. This increases memory usage and significantly impacts performance.
Output 1: Loading list using loops
Example: With Virtualization
To improve performance, we can replace the loop with a Virtualize
component as shown below.
<ul>
<Virtualize Items="@users" ItemSize="130" Context="user">
<li>
....
</li>
</Virtualize>
</ul>
Code Snippet 3: Code snippet with virtualization
Now if you notice below, only the 12 items visible in the viewport load initially, and as the user scrolls, only the visible items update instead of loading the entire list. This significantly reduces the load on the UI and provides a smoother experience.
Output 2: Loop with virtualization
Supporting Classes for the Example
Here is the User
model used in the example
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsEmployeeFTE { get; set; }
}
Code Snippet 4: Model Class User
And here is the UserService
class:
public class UserService
{
public List<User> GetUsers(int count)
{
var users = new List<User>();
for (int i = 0; i <= count; i++)
{
users.Add(new User
{
Id = i,
Name = $"User {i+1}",
Description = $"This is a description for User {i+1}.",
IsEmployeeFTE = i % 2 == 0
});
}
return users;
}
}
Code Snippet 5: UserService
Ensure to register this service in Program.cs
:
builder.Services.AddSingleton<UserService>();
Conclusion
Virtualization in Blazor, implemented via the Virtualize
component, allows developers to handle large datasets effectively by only rendering the visible portion of the list. This approach reduces memory usage, improves application performance, and provides a smoother user experience, especially in data-heavy applications. With a predictable item size and efficient data loading, virtualization makes Blazor applications scalable while ensuring responsive UI interactions.