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

use-lane

v0.2.0

Published

Transition-native data fetching for React 19 — Lane caches the promise behind each key and re-reads it inside transitions, while React owns loading, errors, and optimistic UI.

Downloads

373

Readme

use-lane

npm version license

Transition-native data fetching for React 19. Refetches run inside React transitions — invalidate after a mutation, revalidate on focus, or defer a filter change, and the screen you're looking at stays live while the next data loads. No spinner flash, no torn UI.

React 19 ships the primitives to render async data — use(promise) for data, Suspense for loading, Error Boundaries for errors, transitions for non-blocking updates, and useOptimistic / useActionState for mutations. It doesn't ship the layer underneath: something to cache those promises by key, share one request across components, and re-fetch after a change. Lane is exactly that layer — and nothing else.

const { promise } = useLane(["user", id], ({ signal }) => fetchUser(id, signal));
const user = use(promise); // Suspense owns loading, Error Boundaries own errors

Why Lane

Libraries like SWR and TanStack Query own a resolved-value cache plus their own loading/error/status objects, optimistic patches, and mutation helpers. Lane takes the opposite split: it owns only the promise identity behind each key and lets React own the UI state it was designed to own in React 19.

  • Every update is a transition. SWR and React Query keep the previous screen during a refetch with a library flag (keepPreviousData / placeholderData). Lane keeps each key's promise in React state, so wrapping a key change or an invalidate in startTransition — or useDeferredValuejust works: the same transition you use everywhere else, interruptible, with a real pending flag.
  • One mental model. Mutate the source, invalidate the read, render from the next promise — the same model you already use next to Server Components.
  • No parallel state machine. No data / error / isLoading result object. You read with use(promise); Suspense and Error Boundaries do the rest.
  • No mutation helper, by design. Mutations stay in React primitives, so optimistic UI lives next to the action that triggered it instead of in a global cache that needs rollback semantics.

Requirements

React 19.2+ (Lane uses useEffectEvent). React is a peer dependency.

Install

npm install use-lane
# or: pnpm add use-lane
# or: yarn add use-lane

Quick start

1. Wrap your client tree in a LaneProvider.

"use client";

import { LaneProvider } from "use-lane";

export function Providers({ children }: { children: React.ReactNode }) {
  return <LaneProvider>{children}</LaneProvider>;
}

2. Read with useLane and unwrap with use. Lane returns the promise; a Suspense boundary owns the loading state and an Error Boundary owns the initial-load failure.

"use client";

import { Suspense, use } from "react";
import { useLane } from "use-lane";

function Profile({ userId }: { userId: string }) {
  const { promise } = useLane(["user", userId], async ({ signal }) => {
    const res = await fetch(`/api/users/${userId}`, { signal });
    if (!res.ok) throw new Error("Failed to load user");
    return (await res.json()) as User;
  });

  const user = use(promise);
  return <h1>{user.name}</h1>;
}

export function UserProfile({ userId }: { userId: string }) {
  return (
    <Suspense fallback={<p>Loading…</p>}>
      <Profile userId={userId} />
    </Suspense>
  );
}

3. Converge after a mutation by invalidating the source. Mounted readers re-read through a transition; isTransitionPending tells you it is happening.

"use client";

import { useLaneInstance } from "use-lane";

function RenameButton({ userId }: { userId: string }) {
  const lane = useLaneInstance();

  async function rename(name: string) {
    await fetch(`/api/users/${userId}`, {
      method: "PATCH",
      body: JSON.stringify({ name }),
    });
    // Source changed → re-read the affected key. React renders the next promise.
    lane.invalidate(["user", userId]);
  }

  return <button onClick={() => rename("Ada")}>Rename</button>;
}

Core concepts

  • Transition-native re-reads. Updates run through useTransition, so the previous UI stays mounted and interactive while the next promise resolves — isTransitionPending and isBackgroundPending tell you which is in flight. Pair a key with useDeferredValue for search and filter UIs. (Initial loads with no prior data still suspend to a Suspense fallback.)
  • Keys are structural arrays (["task", id]). They are matched exactly, or by prefix / predicate for scoped operations. Date segments are supported.
  • Invalidation-driven re-reads. invalidate clears the cached promise and notifies mounted readers, which create the next promise from their current loader. Explicit (transition) and automatic (focus / mount / polling, reported as isBackgroundPending) re-reads are kept separate.
  • Stale-on-error. A failed refresh keeps serving the last fulfilled value and reports the failure through refreshError. Only an initial load (no previous value) rejects the promise and reaches the Error Boundary.
  • Authoritative publication. set / update publish server-confirmed data to exact keys; LaneHydration seeds promises from RSC-loaded data and overwrites authoritatively on navigation.
  • Lifecycle built in. Garbage collection (gcTime, default 5 min), retry / retryDelay, refetchInterval polling, and refetchOnFocus / refetchOnMount / refetchOnReconnect revalidation.
  • Optimistic UI stays local. Lane ships no mutation helper; use useOptimistic / useActionState in the component that owns the action.

API at a glance

| Export | Purpose | | --- | --- | | LaneProvider | Provides a Lane instance to the tree; wires focus / reconnect revalidation. | | useLane(key, loader, options?) | Read a key. Returns { promise, refreshError, isTransitionPending, isBackgroundPending, invalidate }. | | useLanePromise(key, loader, options?) | Thin wrapper returning just promise. | | useLaneInstance() | The current Lane instance, for invalidate / set / update / remove from event handlers. | | createLane() | Create a Lane instance manually (e.g. to share one across providers or seed on the server). | | LaneHydration | Apply RSC-loaded snapshots as authoritative seed values. |

Lane instance methods: invalidate / invalidateAll, set, update / updateAll, remove / removeAll. useLane options: staleTime, gcTime, retry, retryDelay, refetchInterval, refetchOnFocus, refetchOnMount, refetchOnReconnect.

See the API reference for full signatures and semantics.

Documentation

License

MIT © Kento Moriwaki