Server‑Side Pagination in Vaadin 24 Grids – Decision Guide
When a Vaadin Flow Grid must display tens of thousands of rows, server‑side pagination keeps the client light and lets the database do the heavy lifting. This guide compares client‑side, server‑side, and hybrid paging...
21 Apr 2026, 23:20 UTC

Decision: Use Server‑Side Pagination for Large Data Sets
When a Vaadin Flow Grid must display tens of thousands of rows, the default client‑side paging—where the entire data set is sent to the browser—quickly becomes a performance bottleneck. Server‑side pagination limits the amount of data transferred per request, keeps the browser’s memory footprint small, and lets the backend apply database‑level filtering and sorting. This guide explains the constraints, compares the three main paging strategies, and walks through a concrete, verifiable implementation.
Constraints & Decision Context
- Data volume: >5,000 rows or more.
- Network latency: Users may be on mobile or slow connections.
- Backend resources: Database can handle range queries; multithreaded access is required.
- User experience: Smooth scrolling with minimal flicker.
- Deployment environment: Clustered application may require stateless DataProvider.
If the dataset is small (<2,000 rows) and the application is single‑user, client‑side paging is acceptable. For larger, multi‑user scenarios, server‑side paging is the default recommendation.
Option Comparison Table
| Paging Strategy | Data Transfer | Memory Footprint | Implementation Complexity | Backend Load | Typical Use‑Case |
|---|---|---|---|---|---|
| Client‑Side Paging | All rows sent once | High on client | Low – default Grid behaviour | High – single bulk query | Small tables, offline use |
| Server‑Side Paging | Page size only per request | Low on client | Medium – custom DataProvider | Low – range queries | Large tables, dynamic filtering |
| Hybrid (prefetch) | Page + prefetch window | Moderate | High – caching logic | Moderate – multiple queries | Very large tables where latency is critical |
Trade‑Off Analysis
- Bandwidth: Server‑side paging reduces the initial payload from megabytes to a few kilobytes per page.
- Latency: Each page request adds a round‑trip; choose a page size (e.g., 100–200 rows) that balances latency and backend load.
- Complexity: Requires a custom
DataProviderthat implementsfetchandsizemethods. Vaadin 24’sPagingDataProvidersimplifies this. - Statefulness: Server‑side providers should be stateless or use a thread‑safe cache to avoid session bloat in clustered setups.
- Browser memory: Client‑side paging can exceed 200 MB in modern browsers for 10,000+ rows, leading to slow rendering.
Concrete Implementation
1. Define a PagingDataProvider
Vaadin 24 ships with PagingDataProvider that automatically handles lazy loading, sorting and filtering. The provider delegates to a PagingQuery that you implement to fetch data from your database.
public class UserPagingProvider extends PagingDataProvider<User, Void> {
private final UserRepository repo; // Spring Data JPA or JDBC
public UserPagingProvider(UserRepository repo) {
super(new PagingQuery<>() {
@Override
public Stream<User> fetch(Query<User, Void> query) {
int offset = query.getOffset();
int limit = query.getLimit();
Sort sort = query.getSort();
return repo.findAll(PageRequest.of(offset / limit, limit, sort)).stream();
}
@Override
public long size(Query<User, Void> query) {
return repo.count();
}
});
}
}
Replace UserRepository with your own data access layer. The offset and limit are calculated by Vaadin based on the requested page.
2. Wire the Grid
Grid<User> grid = new Grid<>(User.class);
UserPagingProvider provider = new UserPagingProvider(userRepository);
grid.setDataProvider(provider);
grid.setPageSize(100); // 100 rows per page
Setting pageSize informs the Grid how many rows to request per page. Vaadin will automatically send a fetch call when the user scrolls to the end of the current page.
3. Enable Sorting and Filtering
Because PagingDataProvider forwards Sort and Filter objects to the fetch method, you can implement database‑level sorting and filtering directly within the repository query. For example, in Spring Data JPA you might use PageRequest.of(..., sort) and a Specification<User> for filtering.
Validation & Benchmarking
To confirm that server‑side paging delivers the expected performance, follow these steps:
- Load a 10,000‑row dataset into your test database.
- Run the Grid with
pageSize=100and note the initial load time (expect < 1 s). - Measure bandwidth using the browser’s network tab: the first page should be ~200 kB.
- Open a second browser tab and scroll to page 50. Verify that a new request is made and the response contains only the requested 100 rows.
- Compare with a client‑side Grid that loads all 10,000 rows: the initial payload should be ~20 MB and the browser memory footprint high.
- Optional: add a simple LRU cache to the
UserPagingProviderand repeat the page‑50 request to ensure the database query is skipped.
Use a tool like jvisualvm or jconsole to monitor server memory consumption during the test. A well‑tuned server‑side provider should keep heap usage below 200 MB for the 10,000‑row test.
Limitations & Practical Checks
- Server‑side paging increases round‑trip latency; if users experience noticeable scrolling delays, consider a hybrid prefetch strategy.
- Ensure the DataProvider is thread‑safe. In a clustered environment, avoid storing state in the provider instance.
- For highly dynamic data where rows change frequently, implement a cache invalidation strategy or use database change notifications.
- Verify that the Grid’s
pageSizematches the database page size to avoid off‑by‑one errors. - Run a quick sanity check: after opening the Grid, open the browser console and look for a
GET /api/users?page=0&size=100request. The response should contain a JSON array of 100 items.
By following this pattern, you can confidently handle large tables in Vaadin Flow while keeping the client lightweight and the backend efficient.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.