How It Works
Virtual Scroll (True Virtualization)
- Constant DOM size - Only ~20 items in DOM regardless of total dataset
- Performance - Can handle millions of items smoothly
- Fixed heights - Requires items to have consistent, known heights
- Use case - Tables, lists with uniform row heights
- DOM strategy - Items added/removed dynamically as you scroll
Infinite Scroll (Lazy Loading)
- Growing DOM - All loaded items stay in DOM
- Performance - Good for hundreds/low thousands of items
- Variable heights - Works with dynamic content heights
- Use case - Feeds, timelines with rich content
- DOM strategy - Items appended, never removed
Virtual Scroll Examples
True virtualization - constant DOM size
Timeline - 5000 Items
Only ~20 items in DOM at any time
Not Yet Supported
True virtual scroll (windowed rendering) requires a custom JS hook that calculates visible indices from scroll position and row height, then only renders those rows. This is planned for a future release.
Why It's Complex in LiveView
LiveView manages the DOM server-side. True virtualization needs client-side DOM manipulation that conflicts with LiveView's diffing. A proper implementation requires a dedicated JS hook that coordinates with the server to only request visible data windows.
Table - 10000 Rows
Smooth scrolling through large datasets
Not Yet Supported
Virtual scroll for tables requires fixed row heights and a hook that renders spacer rows above/below the visible window. This approach works in LiveView but needs careful implementation to avoid conflicts with DOM patching.
Alternative: Server-Side Pagination
For large datasets, use the
<.pager> component with server-side queries. This is more efficient than virtual scroll for most use cases since it transfers only the visible page of data.
Infinite Scroll Example
Lazy loading - items accumulate in DOM. Uses PureAdminInfiniteScroll hook.
Timeline Feed - Infinite Scroll
Loads more items automatically as you scroll (20 of 200 loaded)
-
09:00John Smith performed action #0
-
10:05Emma Davis performed action #1
-
11:10Luna Bonifacio performed action #2
-
12:15Yoan Almedia performed action #3
-
13:20Sarah Chen performed action #4
-
14:25Mike Johnson performed action #5
-
15:30John Smith performed action #6
-
16:35Emma Davis performed action #7
-
17:40Luna Bonifacio performed action #8
-
18:45Yoan Almedia performed action #9
-
09:50Sarah Chen performed action #10
-
10:55Mike Johnson performed action #11
-
11:00John Smith performed action #12
-
12:05Emma Davis performed action #13
-
13:10Luna Bonifacio performed action #14
-
14:15Yoan Almedia performed action #15
-
15:20Sarah Chen performed action #16
-
16:25Mike Johnson performed action #17
-
17:30John Smith performed action #18
-
18:35Emma Davis performed action #19
Infinite Scroll Hook Usage
heex
<div
id="scroll-sentinel"
phx-hook="PureAdminInfiniteScroll"
data-event="load_more"
data-has-more={to_string(@has_more)}
data-throttle="500"
data-root-margin="200px"
>
<.loader :if={@loading} />
</div>
| Attribute | Default | Description |
|---|---|---|
data-event
|
load_more
| LiveView event name to push |
data-has-more
|
true
| Set to "false" to stop observing |
data-root-margin
|
200px
| Preload buffer distance |
data-throttle
|
500
| Minimum ms between triggers |