npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@real-router/preload-plugin

v0.3.0

Published

Preload plugin — trigger data preloading on navigation intent (hover, touch)

Readme

@real-router/preload-plugin

npm npm downloads bundle size License: MIT

Preload on navigation intent for Real-Router. Trigger data preloading when users hover over or touch links — before they click.

// Without plugin — data loads AFTER navigation:
// click → navigate → render → fetch → re-render (waterfall)

// With plugin — data loads BEFORE navigation:
// hover → preload → click → navigate → render (data ready)

Installation

npm install @real-router/preload-plugin

Peer dependency: @real-router/core

Runtime dependency: @real-router/browser-plugin must be registered (provides matchUrl for URL → route resolution).

Quick Start

import { createRouter } from "@real-router/core";
import { browserPluginFactory } from "@real-router/browser-plugin";
import { preloadPluginFactory } from "@real-router/preload-plugin";

const routes = [
  {
    name: "users.profile",
    path: "/users/:id",
    preload: async (params) => {
      await queryClient.prefetchQuery({
        queryKey: ["user", params.id],
        queryFn: () => fetchUser(params.id),
      });
    },
  },
  {
    name: "products.detail",
    path: "/products/:slug",
    preload: async (params) => {
      await productStore.prefetch(params.slug);
    },
  },
];

const router = createRouter(routes);
router.usePlugin(browserPluginFactory(), preloadPluginFactory());

await router.start();

When a user hovers over a <Link routeName="users.profile" routeParams={{ id: '123' }}> for 65ms, the plugin calls preload({ id: '123' }) — warming up your data layer before navigation.

Options

router.usePlugin(
  preloadPluginFactory({
    delay: 100, // Hover debounce in ms (default: 65)
    networkAware: true, // Disable on Save-Data / 2G (default: true)
  }),
);

| Option | Type | Default | Description | | -------------- | --------- | ------- | ---------------------------------------------------- | | delay | number | 65 | Milliseconds to wait before triggering hover preload | | networkAware | boolean | true | Skip preloading on Save-Data or 2G connections |

How It Works

Zero adapter changes

The plugin uses DOM-level event delegation — listeners on document, not on individual <Link> components. No modifications to React, Vue, Preact, Solid, or Svelte adapters.

Intent detection

| Trigger | Event | Timing | Rationale | | --------- | ------------ | ------------------------- | ------------------------------ | | Hover | mouseover | Debounced (configurable) | Filter accidental mouse passes | | Touch | touchstart | ~100ms (scroll detection) | Touch = strong intent signal |

Route resolution

anchor.href → router.matchUrl(href) → State → getRouteConfig(state.name)?.preload → call

External links, routes without preload, and non-matching URLs are silently skipped.

Mobile support

  • Touch preloading: touchstart triggers preload with minimal delay
  • Scroll detection: touchmove with >10px vertical movement cancels pending preload
  • Ghost event suppression: Synthetic mouseover events fired by mobile browsers after touchstart are suppressed (prevents double-preload)

All listeners use { passive: true } — never blocks scrolling.

Network awareness

Preloading is automatically disabled when:

  • navigator.connection.saveData is enabled
  • navigator.connection.effectiveType is 2g or slow-2g

Disable with networkAware: false if your preload functions handle this themselves.

Per-Link Opt-Out

<!-- Disable preloading for a specific link -->
<a href="/heavy-page" data-no-preload>Heavy Page</a>

Works in all frameworks via prop pass-through:

<Link routeName="heavy" data-no-preload>
  Heavy Page
</Link>

Router Extension

The plugin adds one method to the router:

router.getPreloadSettings();
// → { delay: 65, networkAware: true }

Data Layer Integration

The plugin is data-agnostic — it calls your preload function and doesn't care about the result. You control what happens inside:

TanStack Query

{
  name: "users.profile",
  path: "/users/:id",
  preload: async (params) => {
    await queryClient.prefetchQuery({
      queryKey: ["user", params.id],
      queryFn: () => fetchUser(params.id),
    });
  },
}

Zustand / Pinia / Custom Store

{
  name: "products.detail",
  path: "/products/:slug",
  preload: async (params) => {
    await productStore.prefetch(params.slug);
  },
}

Multiple Concerns

{
  name: "dashboard",
  path: "/dashboard",
  preload: async () => {
    await Promise.all([
      queryClient.prefetchQuery({ queryKey: ["stats"], queryFn: fetchStats }),
      queryClient.prefetchQuery({ queryKey: ["recent"], queryFn: fetchRecent }),
    ]);
  },
}

Errors in preload are silently caught — error handling is your data layer's responsibility.

SSR Support

The plugin is SSR-safe — returns an empty plugin object when document is not available:

// Server-side — no errors, no listeners
router.usePlugin(preloadPluginFactory());

Graceful Degradation

Without browser-plugin, router.matchUrl is undefined. The plugin silently skips preloading via optional chaining — no errors, no warnings.

Documentation

Related Packages

| Package | Description | | -------------------------------------------------------------------------------------------- | -------------------------------------- | | @real-router/core | Core router (required peer dependency) | | @real-router/browser-plugin | Browser plugin (provides matchUrl) | | @real-router/lifecycle-plugin | Route-level lifecycle hooks |

Contributing

See contributing guidelines for development setup and PR process.

License

MIT © Oleg Ivanov