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

intent-link

v1.0.14

Published

User target prediction library

Readme

intent-link

A target prediction library for React/Next.js.

intent-link watches real user behavior using kinetic and potential energy and calls onIntent when the user is predicted to interact with a target.

Website · Support Intent Link on Patreon

Consumer architecture

The primary library experience consists of two components:

  1. IntentProvider runs one prediction engine for the application. Add it once to your root layout.
  2. IntentLink is the prebuilt Next.js link. Use it anywhere below the provider when you want to react to predicted intent.

The quick start below shows each component in its natural location: IntentProvider in layout.tsx, followed by an IntentLink inside an application component.

For non-link elements and third-party components, the advanced useIntentTarget hook returns a ref that connects one HTML element to the same prediction engine.

What you do with that signal is up to you. Prefetching a route is one obvious use case, but predicting user intent has plenty of others:

  • Prefetching / preloading — warm a route, an image, or an API call before the click happens.
  • Progressive disclosure — reveal a tooltip, preview card, or "quick view" panel just before the user commits to a click.
  • Analytics — log near-misses and predicted-but-abandoned targets, not just actual clicks, to understand hesitation and drop-off.
  • Adaptive UI — subtly highlight, enlarge, or emphasize the element the user is most likely heading toward.
  • Expensive-action warmup — spin up a checkout session, open a websocket, or start an expensive computation slightly ahead of an explicit user action.
  • A/B and UX research — measure "intent-to-click" as a softer signal than clicks alone.

It ships with IntentLink, a drop-in replacement for next/link.

Install

npm install intent-link
yarn add intent-link
pnpm add intent-link

Compatibility

| Dependency | Supported range | Continuously tested | |---|---|---| | React | 18.x and 19.x | React 19 | | React DOM | 18.x and 19.x | React DOM 19 | | Next.js | 13.x through 16.x | Next.js 16 | | Node.js | Current Next.js requirement | Node.js 20 and 22 in CI |

The package is a client-side integration and supports both the Next.js App Router and Pages Router wherever next/link and client components are available.

Quick start

Wrap your app once with IntentProvider. It runs the shared prediction engine for all registered targets below it:

// app/layout.js
import { IntentProvider } from "intent-link";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <IntentProvider>
          {children}
        </IntentProvider>
      </body>
    </html>
  );
}

Use case: prefetching (the built-in component)

import { IntentLink } from "intent-link";
import { useRouter } from "next/navigation";

function ProductCard({ href }) {
  const router = useRouter();

  return (
    <IntentLink href={href} onIntent={() => router.prefetch(href)}>
      View product
    </IntentLink>
  );
}

onIntent fires once when the model's action criterion is met—not on every mouse move.

Use case: something other than prefetching

onIntent is just a callback — nothing about it is prefetch-specific. For example, warming up a preview panel instead:

<IntentLink
  href="/pricing"
  onIntent={() => setShowPricingPreview(true)}
>
  Pricing
</IntentLink>

Or logging predicted-but-unclicked targets for analytics:

<IntentLink
  href="/upgrade"
  onIntent={() => track("predicted_intent", { href: "/upgrade" })}
>
  Upgrade
</IntentLink>

If you want target prediction without next/link's routing behavior at all—such as a preview on a button or card—use useIntentTarget.

API

<IntentProvider>

Context provider that runs the prediction engine. Mount it once, near the root of your app (typically in layout.js/layout.tsx). Everything inside it can use IntentLink or useIntentTarget.

<IntentProvider>{children}</IntentProvider>

No props.

<IntentLink>

A target-prediction-aware drop-in replacement for next/link. Accepts everything next/link accepts (href, replace, scroll, prefetch, etc.), plus every native <a> attribute (className, style, target, aria-*, and so on) — anything not listed below is passed straight through to the rendered anchor. ref is forwarded too, just like next/link.

| Prop | Type | Default | Description | |---|---|---|---| | href | string \| UrlObject | — | Same as next/link. Required. | | importance | "high" \| "medium" \| "low" | "medium" | How readily predicted intent alone should trigger onIntent for this target. Higher importance triggers sooner. | | cost | "high" \| "medium" \| "low" | "low" | The cost of acting on a false positive. Raise this if whatever onIntent does is expensive (an API call, a websocket) rather than cheap (a router.prefetch). | | onIntent | () => void | — | Called once when the model decides that acting on predicted intent is worthwhile. Resets after the user moves away and can fire again on a later approach. | | ...rest | — | — | Any other next/link or anchor prop — className, style, target, ref, etc. |

<IntentLink
  href="/checkout"
  importance="high"
  cost="high"
  className="btn btn-primary"
  onIntent={() => router.prefetch("/checkout")}
>
  Checkout
</IntentLink>

How importance and cost interact

Raise importance when the target deserves earlier action, such as a prominent call to action. Raise cost when an incorrect trigger would be expensive or disruptive. The library keeps the underlying decision calculation private and calls onIntent only when its action criterion is met.

Suggested starting points:

| Action | Importance | Cost | |---|---|---| | Route or image prefetch | medium | low | | Passive preview or highlight | medium | low | | API request | medium | medium | | Expensive session or connection warmup | high | high |

These are product-level controls, not confidence scores. Start with the defaults and raise cost when a false trigger would consume meaningful resources.

Custom targets

IntentLink is the recommended prebuilt component for navigation. When the target is not a Next.js link, use useIntentTarget. It returns a ref; attach that ref to the HTML element whose intent you want to observe.

Basic ref example

"use client";

import { useIntentTarget } from "intent-link";

export function PreviewButton() {
  const intentRef = useIntentTarget<HTMLButtonElement>({
    onIntent: () => preloadProductPreview(),
  });

  return (
    <button ref={intentRef}>
      Preview product
    </button>
  );
}

Registration, model state, decision-making, one-shot triggering, resetting, and cleanup remain internal.

Building a reusable IntentButton

If an application contains many predictive buttons, wrap the hook once and reuse the resulting component:

"use client";

import type { ButtonHTMLAttributes } from "react";
import {
  useIntentTarget,
  type UseIntentTargetOptions,
} from "intent-link";

type IntentButtonProps =
  ButtonHTMLAttributes<HTMLButtonElement> &
  UseIntentTargetOptions;

export function IntentButton({
  onIntent,
  importance,
  cost,
  children,
  ...buttonProps
}: IntentButtonProps) {
  const intentRef = useIntentTarget<HTMLButtonElement>({
    onIntent,
    importance,
    cost,
  });

  return (
    <button ref={intentRef} {...buttonProps}>
      {children}
    </button>
  );
}

Usage:

<IntentButton
  className="preview-button"
  onIntent={preloadPreview}
>
  Preview
</IntentButton>

Building a reusable IntentArticle

The same pattern works for cards and other semantic containers:

"use client";

import type { HTMLAttributes } from "react";
import {
  useIntentTarget,
  type UseIntentTargetOptions,
} from "intent-link";

type IntentArticleProps =
  HTMLAttributes<HTMLElement> &
  UseIntentTargetOptions;

export function IntentArticle({
  onIntent,
  importance,
  cost,
  children,
  ...articleProps
}: IntentArticleProps) {
  const intentRef = useIntentTarget<HTMLElement>({
    onIntent,
    importance,
    cost,
  });

  return (
    <article ref={intentRef} {...articleProps}>
      {children}
    </article>
  );
}

Usage:

<IntentArticle
  className="product-card"
  onIntent={() => preloadProduct(productId)}
>
  <ProductImage />
  <ProductDetails />
</IntentArticle>

Third-party components

If a third-party component forwards its ref to a real HTML element, attach the intent ref directly:

const intentRef = useIntentTarget<HTMLDivElement>({
  onIntent: prepareCarousel,
});

return (
  <ThirdPartyCarousel ref={intentRef}>
    {slides}
  </ThirdPartyCarousel>
);

If the third-party component does not forward its ref, wrap it in a native element and observe that wrapper:

const intentRef = useIntentTarget<HTMLDivElement>({
  onIntent: prepareCarousel,
});

return (
  <div ref={intentRef}>
    <ThirdPartyCarousel>{slides}</ThirdPartyCarousel>
  </div>
);

Mobile behavior

On touch devices there's no cursor to track, so intent-link falls back to tracking scroll velocity and deceleration instead — a user slowing their scroll near a target is treated as a signal of intent, feeding the same probability model.

TypeScript

Fully typed. Import types directly:

import type { IntentLinkProps, UseIntentTargetOptions } from "intent-link";

License

MIT

Support the project

intent-link is free and open source. If it is useful to you, you can support its continued development on Patreon.

Contributors

  • Uzafir Ahmad Rafaq
  • Muaz Hassan
  • Ali Muzaffar