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-dead-code-canary

v0.1.0

Published

Zero-dependency React canary that flags unused UI modules in production by tracking mounts, visibility, and interactions locally.

Readme

react-dead-code-canary

Zero-dependency React utility that flags unused UI modules in production. Wrap a suspicious component in <Canary>, ship it, and let real users prove whether it is ever seen or touched. Everything is recorded locally in the visitor's browser; you decide if/where telemetry is sent.

Features

  • Drop-in wrapper — <Canary id="..."> renders children through a display: contents div, so layout is untouched.
  • Three signals per component — mounts (mountCount, firstMountedAt, lastMountedAt), visibility via IntersectionObserver (lastSeenAt), and real usage via capture-phase click / focusin / change listeners (lastInteractedAt).
  • Structured reports — useCanaryReport() aggregates every record into a JSON-friendly CanaryReport with a computed deadComponents list.
  • Bring-your-own telemetry — onReportSend fires a debounced report once per session per id; flushCanaryReport(sender) sends on demand. Nothing is transmitted unless you wire it up.
  • Safe everywhere — SSR-safe ("use client" banner, every browser API guarded), localStorage wrapped in try/catch with an in-memory fallback, zero runtime dependencies, tree-shakeable (sideEffects: false), ESM + CJS + types.

Install

npm install react-dead-code-canary
# peer deps: react@^18||^19, react-dom@^18||^19

Quickstart

"use client"; // already applied inside the package; shown here for clarity in Next.js apps

import { Canary, useCanaryReport, flushCanaryReport, clearCanaryData } from "react-dead-code-canary";

// 1. Wrap anything you suspect is dead weight.
export function Sidebar() {
  return (
    <Canary
      id="legacy-export-panel"
      thresholdDays={30}
      onReportSend={(report) =>
        fetch("/api/dead-code", {
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify(report),
        })
      }
    >
      <LegacyExportPanel />
    </Canary>
  );
}

// 2. Read the aggregated report anywhere (e.g. an internal audit page).
export function AuditPage() {
  const { entries, deadComponents, refresh } = useCanaryReport({ thresholdDays: 30 });
  return (
    <>
      <button onClick={refresh}>Refresh</button>
      <button onClick={() => flushCanaryReport((r) => console.log(r))}>Send now</button>
      <button onClick={clearCanaryData}>Reset</button>
      <pre>{JSON.stringify(deadComponents, null, 2)}</pre>
      <p>{entries.length} tracked components</p>
    </>
  );
}

A full interactive demo lives in example/DeadCodeAuditDashboard.tsx (run steps in PUBLISH-AND-TEST.md).

API

<Canary> props

| Prop | Type | Required | Description | | --- | --- | --- | --- | | id | string | yes | Stable, globally unique id for the wrapped module. Used as the storage key (rdcc:v1:<id>). | | thresholdDays | number | yes | Days of inactivity before this component counts as dead in reports sent via onReportSend. | | onReportSend | (report: CanaryReport) => void \| Promise<void> | no | Receives the aggregated report, debounced ~3s after mount, at most once per JS session per id. Failures are logged, never thrown. | | disabled | boolean | no | Render children untouched and record nothing (default false). | | children | ReactNode | yes | The UI module under observation. |

Exports

| Export | Kind | Description | | --- | --- | --- | | Canary | component | Tracking wrapper (see props above). | | useCanaryReport(options?) | hook | Returns CanaryReport & { refresh(): void }. Snapshot is computed on mount and when options.thresholdDays changes; call refresh() to re-read storage. | | flushCanaryReport(sender, options?) | function | Builds a fresh report, awaits sender(report), returns the report. Logs and rethrows sender failures (no debounce / session guard). | | clearCanaryData() | function | Deletes every rdcc:v1:* record and re-arms the once-per-session onReportSend guard. | | buildCanaryReport(thresholdDays?) | function | Pure report builder used by the hook and senders. | | isRecordDead(record, now, thresholdDays) | function | The dead-detection predicate (see rule below). | | normalizeThresholdDays(value) | function | Clamps invalid thresholds to the default (30). | | getCanaryStorage() / createLocalStorageStorage(store) / createInMemoryStorage() | functions | Storage backend access for advanced use/testing. | | DEFAULT_THRESHOLD_DAYS, STORAGE_KEY_PREFIX | constants | 30 and "rdcc:v1:". | | CanaryRecord, CanaryReport, CanaryStorage, CanaryReportSender, CanaryProps, StorageLike | types | Full TypeScript surface. |

Record shape

interface CanaryRecord {
  id: string;
  firstMountedAt: number;   // epoch ms
  lastMountedAt: number;    // epoch ms
  mountCount: number;
  lastSeenAt: number | null;       // IntersectionObserver
  lastInteractedAt: number | null; // click / focusin / change
}

The dead-detection rule (exact)

A component is classified DEAD when both conditions hold at report time:

now - max(lastInteractedAt ?? 0, firstMountedAt) >= thresholdDays * 86_400_000
AND lastInteractedAt === null   // it has never been interacted with

In plain words: it has never received a click/focus/change, and at least thresholdDays have passed since it first mounted. Consequences worth knowing:

  • One single interaction — ever — permanently exempts a component from the dead list.
  • Being seen (lastSeenAt) does not rescue a component; visibility is reported for context only.
  • A freshly shipped canary is never flagged before thresholdDays elapse, so new code gets a fair observation window.
  • The comparison is inclusive (>=), and thresholdDays may be 0 (useful in demos/tests: every never-interacted component is flagged immediately).

Notes & caveats

  • Per-browser data. Records live in each visitor's localStorage, so a component is only truly dead org-wide when reports streamed from many sessions (via onReportSend) agree.
  • React StrictMode double-mounts in development, which inflates mountCount by 2 per load there. Production numbers are exact.
  • If IntersectionObserver is missing, visibility tracking is skipped (lastSeenAt stays null); mounts and interactions still work. If localStorage is unavailable (SSR, privacy mode), an in-memory fallback keeps the API functional for the session.
  • The observer watches the element children present at mount; children appended much later are not re-observed.

Privacy

This package stores only component id strings and numeric timestamps, locally, in the visitor's own browser (localStorage keys prefixed rdcc:v1:). No user input, no content, no cookies, no fingerprinting, and no network calls of any kind unless you implement onReportSend / flushCanaryReport — in which case the payload is exactly the CanaryReport JSON documented above. clearCanaryData() deletes everything.

License

MIT © 2026 Dinesh Kumar