Skip to content

Predictive prefetch

The idle trigger preloads everything during quiet moments. Predictive prefetch is the selective version: it watches what the user does, scores each feature, and prefetches only the ones likely to matter.

It’s optional, and most apps don’t need it. When you do, it takes three calls.

import { createPredictivePrefetcher, recordInteraction } from "mountly";
const prefetcher = createPredictivePrefetcher({
features: [signupFeature, paymentFeature, lightboxFeature],
threshold: 0.4, // score 0..1 above which we prefetch
maxConcurrent: 2, // never preload more than two in parallel
});
// Tell the prefetcher when the user shows interest in a feature.
document.querySelector("#cta")?.addEventListener("mouseenter", () => {
recordInteraction("signup-card", "hover");
});

The prefetcher runs feature.preload() on its own clock, weighted by what recordInteraction has seen.

Mountly ships two prefetchers:

Scores features by proximity to the viewport. Features just below the fold preload before the user scrolls to them.

import { createScrollPrefetcher } from "mountly";
createScrollPrefetcher({
features: [embedFeature, panelFeature],
rootMargin: "200px", // preload 200px before they enter viewport
});

Uses cursor velocity and direction to predict which trigger is about to be hovered. Useful for dense interfaces (toolbars, menu bars) where the user’s path implies intent.

import { createMouseTrailPrefetcher } from "mountly";
createMouseTrailPrefetcher({
features: toolbarFeatures,
lookahead: 120, // ms of trajectory to project forward
});

recordInteraction(moduleId, kind) accumulates per-session data. getInteractionHistory() exposes it; resetInteractionHistory() clears.

import { recordInteraction, getInteractionHistory, resetInteractionHistory } from "mountly";
recordInteraction("signup-card", "hover");
recordInteraction("signup-card", "mount");
console.log(getInteractionHistory());
// → { "signup-card": { hovers: 1, mounts: 1, lastSeen: 1714..., score: 0.62 } }

The history is in-memory only. Persist it across sessions yourself if you want, using localStorage, your analytics pipeline, or whatever you already have.

  • Your app has fewer than ~3 features per page. The win is too small to justify the complexity.
  • You’re on a metered or 3G-first audience. Bytes the user doesn’t ask for are bytes you should not be sending.
  • You’re already at < 100 KB total page weight. There’s nothing to optimise.

When in doubt, don’t ship it. The default attach({ preloadOn: "hover" }) already handles most cases.