Skip to content

Scroll depth

The scroll subpath emits a scroll_depth event at each of four thresholds: 25 %, 50 %, 75 %, 100 %. Each threshold fires at most once per pageview. Bundle target: under 1 kB gzip.

import { installScrollDepth } from '@syntarie/tracking/scroll';
import { send } from '@syntarie/tracking';
const teardown = installScrollDepth(send);

The listener is requestAnimationFrame-coalesced so it does not contend with the main thread on noisy scroll wheels or trackpad inertia.

{
"type": "scroll_depth",
"url": "https://example.com/blog/post",
"ts": 1714665600000,
"props": { "depth": 50 }
}

props.depth is one of 25, 50, 75, 100.

Pages where the document height does not exceed the viewport height (i.e. no scrollbar) are suppressed entirely. The user has nothing to scroll; the 50 %-or-better thresholds would fire on every pageview, polluting the metric.

Reset the per-pageview bookkeeping when your router transitions:

import { resetScrollDepth } from '@syntarie/tracking/scroll';
router.beforeEach(() => {
resetScrollDepth();
});

Without the reset, scrolling 50 % on page A then navigating to page B and scrolling 50 % there would not emit on B (the threshold for that pageview already fired). The reset makes each route’s scroll a fresh start.