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

react-lazy-chunk-recover

v0.1.2

Published

A React.lazy-compatible loader that recovers from stale code-split chunks after deployments.

Readme

react-lazy-chunk-recover

A small, typed replacement for React.lazy that attempts one guarded reload when a code-split module fails to load. Applications with a current asset manifest can optionally supply an alternate import before the reload.

Open tab uses admin-oldHash.js
             |
New deploy removes oldHash.js
             |
Route navigation -> dynamic import fails -> one guarded page reload -> current index.html

Install

npm install react-lazy-chunk-recover

react is a peer dependency. The package supports React >=16.8; development and CI target Node 18+ and current React.

Basic usage

It replaces lazy one-for-one.

- import { lazy } from 'react';
+ import { lazyWithRetry } from 'react-lazy-chunk-recover';

- const Admin = lazy(() => import('./Admin'));
+ const Admin = lazyWithRetry(() => import('./Admin'));

Use it with your existing Suspense boundary:

const Settings = lazyWithRetry(() => import('./Settings'));

<Suspense fallback={<Spinner />}>
  <Settings />
</Suspense>

Options

| Option | Default | Purpose | | --- | --- | --- | | isRecoverableError | Built-in cross-browser matcher | Identifies stale chunk errors. | | onStaleChunk | window.location.reload() | Custom recovery callback. The original error is then rethrown. | | retries | 0 | In-place attempts before recovery. | | retryDelayMs | 300 | First retry delay in ms; subsequent delays double. | | storageKey | __lazy_retry_reload__ | sessionStorage key for the reload guard. | | moduleKey | Shared guard | Stable component identifier. Isolates guards so a different lazy component cannot clear this component's recovery attempt. | | getFreshModule | None | Optional alternate loader called once after retries. Return a { default: Component } module from a trusted current asset URL. |

const Reports = lazyWithRetry(() => import('./Reports'), {
  retries: 2,
  retryDelayMs: 250,
  onStaleChunk(error) {
    console.warn('A deployment changed this route chunk.', error);
    window.location.assign('/');
  },
});

For apps with multiple lazy routes, give each route a stable moduleKey:

const Admin = lazyWithRetry(() => import('./Admin'), { moduleKey: 'Admin' });
const Reports = lazyWithRetry(() => import('./Reports'), { moduleKey: 'Reports' });

An app that publishes a fresh asset manifest can supply an alternate import. The package cannot discover a new hashed chunk URL from a failed import() by itself; your app must provide the URL and ensure it is trusted and compatible with the running app:

const Admin = lazyWithRetry(() => import('./Admin'), {
  moduleKey: 'Admin',
  getFreshModule: async () => {
    const response = await fetch('/asset-manifest.json', { cache: 'no-store' });
    if (!response.ok) throw new Error('Could not load the current asset manifest');
    const manifest = (await response.json()) as { admin: string };
    // Only import URLs produced by your own trusted manifest.
    return import(/* @vite-ignore */ manifest.admin);
  },
});

This advanced path depends on the bundler and deployment layout. Keep old hashed assets available during rollout when possible. For Vite, consider its vite:preloadError event for failures outside React.lazy and serve HTML with Cache-Control: no-cache.

React Router v6/v7

Use a normal lazy component as the route element; Router and the bundler do not need special integration.

import { Route, Routes } from 'react-router-dom';
import { lazyWithRetry } from 'react-lazy-chunk-recover';

const Admin = lazyWithRetry(() => import('./routes/Admin'));

export function AppRoutes() {
  return (
    <Suspense fallback={<div>Loading…</div>}>
      <Routes>
        <Route path="/admin" element={<Admin />} />
      </Routes>
    </Suspense>
  );
}

For React Router's lazy route-module API, keep Router's own loader but wrap individual dynamically imported UI components with lazyWithRetry when they are rendered. The package supports dynamic-import error messages emitted by Vite, Webpack, Rspack, and modern Safari/Firefox/Chromium browsers.

Combine with an Error Boundary

The first recoverable failure reloads the page. A repeated failure after that reload is deliberately rethrown so an Error Boundary can show a useful fallback instead of the application looping forever.

<AppErrorBoundary fallback={<p>Please refresh or try again later.</p>}>
  <Suspense fallback={<Spinner />}>
    <Admin />
  </Suspense>
</AppErrorBoundary>

How it works

When a dynamic import fails with a recognized load-error message, the loader first makes the configured in-place retries. If supplied, getFreshModule gets one chance to load a different module URL. It then writes a marker to sessionStorage and reloads. If another load error happens while the marker remains, the import error propagates to React instead of triggering another reload. Repeating the same import may still hit browser caching; retries are most useful for temporary network failures, not missing hashed assets.

A successful load clears the marker for its moduleKey (or the shared default key), allowing a later deployment to recover normally. Browser access is guarded, so during SSR the original import error is rethrown. If sessionStorage is unavailable, automatic reload is disabled to avoid an unbounded reload loop; the error reaches your Error Boundary.