Skip to content

Scroll Seek Placeholders

The scrollSeekConfiguration property allows you to render a placeholder element instead of the actual item if the user scrolls too fast.

This improves scrolling performance and delays the actual load of data from the server.

import { Virtuoso } from 'react-virtuoso'
import { useMemo, useState } from 'react'

export default function App() {
  const randomHeights = useMemo(
    () =>
      Array(10)
        .fill(true)
        .map(() => Math.round(Math.random() * 14) + 1),
    []
  );


  // use the visible range to provide information
  // about the list current position.
  const [visibleRange, setVisibleRange] = useState(["-", "-"]);

  return (
    <div style={{ display: "flex", flexDirection: 'column', height: '100%' }}>
      <div>
        Current visible range:{" "}
        <div>
          <strong>
            {visibleRange[0]} - {visibleRange[1]}
          </strong>
        </div>{" "}
      </div>

      <div style={{ flex: 1 }}>
        <Virtuoso
          context={{ randomHeights }}
          style={{ height: '100%' }}
          totalCount={1000}
          itemContent={(index ) => <div>Item {index}</div>}
          components={{ ScrollSeekPlaceholder }}
          scrollSeekConfiguration={{
            enter: (velocity) => Math.abs(velocity) > 50,
            exit: (velocity) => {
              const shouldExit = Math.abs(velocity) < 10;
              if (shouldExit) {
                setVisibleRange(["-", "-"]);
              }
              return shouldExit;
            },
            change: (_velocity, { startIndex, endIndex }) => setVisibleRange([startIndex.toString(), endIndex.toString()]),
          }}
        />
      </div>
    </div>
  )
}

// You can use index to randomize
// and make the placeholder list more organic.
// the height passed is the one measured for the real item.
// the placeholder should be the same size.
const ScrollSeekPlaceholder =  ({ height, index, context: { randomHeights }}) => (
  <div
    style={{
      height,
      padding: "8px",
      boxSizing: "border-box",
      overflow: "hidden",
    }}
  >
    <div
      style={{
        background: index % 2 ? "#ccc": "#eee",
        height: randomHeights[index % 10],
      }}
    ></div>
  </div>
)