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

@usefy/use-memory-monitor

v0.2.5

Published

A React hook for real-time browser memory monitoring with leak detection

Readme


Overview

@usefy/use-memory-monitor is a comprehensive React hook for monitoring browser memory usage in real-time. It provides heap metrics, memory leak detection, threshold-based alerts, history tracking, and snapshot comparison capabilities.

Part of the @usefy ecosystem — a collection of production-ready React hooks designed for modern applications.

Why use-memory-monitor?

  • Real-time Monitoring — Track heap usage, total heap, and memory limits in real-time
  • Leak Detection — Automatic memory leak detection using linear regression analysis
  • Threshold Alerts — Configure custom thresholds for low/medium/high/critical severity levels
  • Memory Snapshots — Take snapshots and compare memory usage between different points in time
  • History Tracking — Maintain a circular buffer of historical memory data with trend analysis
  • Tab Visibility Optimization — Automatically pauses monitoring when tab is hidden
  • TypeScript First — Full type safety with comprehensive exported interfaces
  • SSR Compatible — Safe to use with Next.js, Remix, and other SSR frameworks
  • Browser Fallbacks — Graceful degradation in browsers without full API support
  • Well Tested — Comprehensive test coverage (175 tests) with Vitest

Installation

# npm
npm install @usefy/use-memory-monitor

# yarn
yarn add @usefy/use-memory-monitor

# pnpm
pnpm add @usefy/use-memory-monitor

Peer Dependencies

This package requires React 18 or 19:

{
  "peerDependencies": {
    "react": "^18.0.0 || ^19.0.0"
  }
}

Quick Start

import { useMemoryMonitor } from "@usefy/use-memory-monitor";

function MemoryMonitor() {
  const {
    heapUsed,
    heapTotal,
    heapLimit,
    isSupported,
    severity,
    formatted,
  } = useMemoryMonitor({
    interval: 1000,
    autoStart: true,
  });

  if (!isSupported) {
    return <div>Memory monitoring not supported in this browser</div>;
  }

  return (
    <div>
      <h2>Memory Usage</h2>
      <p>Heap Used: {formatted.heapUsed}</p>
      <p>Heap Total: {formatted.heapTotal}</p>
      <p>Heap Limit: {formatted.heapLimit}</p>
      <p>Severity: {severity}</p>
    </div>
  );
}

Browser Support

Full Support (performance.memory API)

  • Chrome (all versions)
  • Edge (Chromium-based)

Limited Support (DOM-only metrics)

  • ⚠️ Firefox - DOM tracking only, no heap metrics
  • ⚠️ Safari - DOM tracking only, no heap metrics
  • ⚠️ Other browsers - Fallback strategies available

Browser Detection

The hook automatically detects browser capabilities and adjusts functionality:

const { isSupported, getUnsupportedInfo, getBrowserSupport } = useMemoryMonitor();

if (!isSupported) {
  const info = getUnsupportedInfo();
  console.log("Reason:", info.reason); // 'no-api' | 'browser-restriction' | etc.
  console.log("Browser:", info.browser);
  console.log("Fallbacks:", info.availableFallbacks);
}

const support = getBrowserSupport();
console.log("Support level:", support.level); // 'full' | 'partial' | 'none'
console.log("Available metrics:", support.availableMetrics);

API Reference

useMemoryMonitor(options?)

A hook that monitors browser memory usage in real-time with leak detection and threshold alerts.

Parameters

| Parameter | Type | Default | Description | | --------- | ----------------------------- | ------- | ---------------------------------- | | options | UseMemoryMonitorOptions | {} | Configuration options (see below) |

Options (UseMemoryMonitorOptions)

| Option | Type | Default | Description | | -------------------- | ----------------------- | -------- | ------------------------------------------------ | | interval | number | 1000 | Polling interval in milliseconds | | autoStart | boolean | true | Start monitoring automatically on mount | | enabled | boolean | true | Enable/disable the hook | | enableHistory | boolean | true | Track memory history | | historySize | number | 50 | Maximum number of history entries | | thresholds | ThresholdOptions | See below | Memory usage thresholds | | leakDetection | LeakDetectionOptions | See below | Leak detection configuration | | pauseWhenHidden | boolean | true | Pause monitoring when tab is hidden | | onThresholdChange | (severity) => void | - | Callback when severity level changes | | onMemoryUpdate | (memory) => void | - | Callback on each memory update | | onLeakDetected | (analysis) => void | - | Callback when memory leak is detected | | onUnsupported | (info) => void | - | Callback when memory API is not supported |

Default Thresholds
{
  medium: 60,    // Yellow alert at 60% usage
  high: 80,      // Orange alert at 80% usage
  critical: 90,  // Red alert at 90% usage
}
Default Leak Detection
{
  enabled: true,
  sensitivity: "medium",  // 'low' | 'medium' | 'high'
  sampleSize: 10,
  minDuration: 30000,     // 30 seconds
}

Returns UseMemoryMonitorReturn

| Property | Type | Description | | ---------------------- | ------------------------------------ | -------------------------------------------- | | memory | MemoryInfo \| null | Current memory information | | heapUsed | number \| null | Used heap size in bytes | | heapTotal | number \| null | Total heap size in bytes | | heapLimit | number \| null | Heap size limit in bytes | | isSupported | boolean | Whether memory monitoring is supported | | isMonitoring | boolean | Whether currently monitoring | | isLeakDetected | boolean | Whether memory leak is detected | | severity | Severity | Current severity level | | history | MemoryInfo[] | Historical memory data | | trend | Trend | Memory usage trend | | formatted | FormattedMemory | Human-readable formatted values | | start | () => void | Start monitoring | | stop | () => void | Stop monitoring | | takeSnapshot | (id: string) => MemorySnapshot \| null | Take a memory snapshot | | compareSnapshots | (id1, id2) => MemoryDifference \| null | Compare two snapshots | | clearSnapshots | () => void | Clear all snapshots | | getAllSnapshots | () => MemorySnapshot[] | Get all snapshots | | clearHistory | () => void | Clear history buffer | | requestGC | () => void | Request garbage collection (hint only) | | getLeakAnalysis | () => LeakAnalysis \| null | Get current leak analysis | | getBrowserSupport | () => BrowserSupport | Get browser support information | | getUnsupportedInfo | () => UnsupportedInfo | Get info about why monitoring is unsupported |

Types

Severity

type Severity = "low" | "medium" | "high" | "critical";

Trend

type Trend = "increasing" | "stable" | "decreasing";

MemoryInfo

interface MemoryInfo {
  heapUsed: number | null;
  heapTotal: number | null;
  heapLimit: number | null;
  timestamp: number;
  domNodes?: number | null;
  eventListeners?: number | null;
}

FormattedMemory

interface FormattedMemory {
  heapUsed: string;
  heapTotal: string;
  heapLimit: string;
  domNodes?: string;
  eventListeners?: string;
}

Examples

Basic Memory Monitoring

import { useMemoryMonitor } from "@usefy/use-memory-monitor";

function BasicMonitor() {
  const { formatted, severity, isMonitoring, start, stop } = useMemoryMonitor({
    interval: 1000,
    autoStart: true,
  });

  return (
    <div>
      <h2>Memory Usage</h2>
      <p>Used: {formatted.heapUsed}</p>
      <p>Total: {formatted.heapTotal}</p>
      <p>Severity: {severity}</p>

      <button onClick={isMonitoring ? stop : start}>
        {isMonitoring ? "Stop" : "Start"}
      </button>
    </div>
  );
}

Memory Leak Detection

import { useMemoryMonitor } from "@usefy/use-memory-monitor";

function LeakDetector() {
  const { isLeakDetected, getLeakAnalysis, formatted } = useMemoryMonitor({
    interval: 1000,
    leakDetection: {
      enabled: true,
      sensitivity: "high",
      sampleSize: 15,
      minDuration: 20000, // 20 seconds
    },
    onLeakDetected: (analysis) => {
      console.warn("Memory leak detected!", {
        probability: analysis.probability,
        confidence: analysis.confidence,
        slope: analysis.slope,
      });
    },
  });

  const analysis = getLeakAnalysis();

  return (
    <div>
      <h2>Leak Detection</h2>
      {isLeakDetected && (
        <div className="alert">
          ⚠️ Memory leak detected!
          <p>Probability: {(analysis?.probability ?? 0).toFixed(2)}%</p>
          <p>Confidence: {analysis?.confidence}</p>
        </div>
      )}
      <p>Current Usage: {formatted.heapUsed}</p>
    </div>
  );
}

How Leak Detection Works

The hook uses an Enhanced Multi-Factor Analysis algorithm to detect memory leaks accurately while minimizing false positives:

Algorithm Overview
  1. Data Collection: Requires minimum 10 samples over 30+ seconds of observation
  2. GC Event Detection: Identifies garbage collection events by detecting significant memory drops (≥10%)
  3. Baseline Calculation: Calculates baseline from post-GC memory values
  4. Baseline Trend Analysis: Monitors if post-GC baselines are increasing over time
  5. Weighted Probability Calculation: Combines multiple factors for accurate detection
Probability Factors (Weighted Scoring)

| Factor | Max Points | Description | |--------|------------|-------------| | Slope | 30 | How fast memory is growing | | R² Fit | 20 | How consistent the growth pattern is | | GC Ineffectiveness | 25 | Whether GC fails to reclaim memory | | Observation Time | 15 | Longer observation = more confidence | | Baseline Growth | 10 | Whether post-GC baseline is rising |

Total: 100 points maximum

Key Principles
  • GC-Aware: True leaks persist even after garbage collection cycles
  • Time-Based: Requires sufficient observation time before making judgment
  • Baseline Tracking: Monitors if the memory "floor" after GC is rising
  • False Positive Reduction: If GC is effective and baseline is stable, probability is reduced
Leak Detection Flow:
1. Collect samples (min 10, min 30 seconds)
   ↓
2. Detect GC events (memory drops ≥10%)
   ↓
3. Calculate baseline (average post-GC memory)
   ↓
4. Analyze baseline trend (is it rising?)
   ↓
5. Calculate weighted probability
   ↓
6. Apply corrections (GC effectiveness, trend)
   ↓
7. Final determination (≥70% = leak detected)
Sensitivity Levels

| Sensitivity | Min Slope | Min R² | Min GC Cycles | Min Observation | |-------------|-----------|--------|---------------|-----------------| | low | 100KB/sample | 0.8 | 3 | 60 seconds | | medium | 50KB/sample | 0.7 | 2 | 30 seconds | | high | 10KB/sample | 0.6 | 1 | 15 seconds |

LeakAnalysis Response

The LeakAnalysis object now includes detailed analysis information:

interface LeakAnalysis {
  isLeaking: boolean;           // Whether a leak is detected (probability ≥ 70%)
  probability: number;          // Leak probability (0-100)
  confidence: number;           // Analysis confidence (0-100)
  trend: Trend;                 // 'increasing' | 'stable' | 'decreasing'
  averageGrowth: number;        // Bytes per sample
  rSquared: number;             // Regression fit quality
  observationTime?: number;     // Total observation time in ms
  gcAnalysis?: {
    gcEventCount: number;       // Number of GC events detected
    avgRecoveryRatio: number;   // Average memory recovered per GC
    isGCEffective: boolean;     // Whether GC is reclaiming memory
  };
  baselineAnalysis?: {
    baselineHeap: number;       // Average post-GC memory
    currentHeap: number;        // Current heap usage
    growthRatio: number;        // Growth from baseline
    isSignificantGrowth: boolean;
  };
  factors?: {
    slopeContribution: number;
    rSquaredContribution: number;
    gcContribution: number;
    timeContribution: number;
    baselineContribution: number;
  };
  recommendation?: string;      // Human-readable recommendation
}

Threshold Alerts

import { useMemoryMonitor } from "@usefy/use-memory-monitor";

function ThresholdMonitor() {
  const { severity, heapUsed, heapLimit, formatted } = useMemoryMonitor({
    interval: 1000,
    thresholds: {
      medium: 50,
      high: 75,
      critical: 90,
    },
    onThresholdChange: (newSeverity) => {
      if (newSeverity === "critical") {
        alert("Critical memory usage!");
      }
    },
  });

  const usagePercent = heapUsed && heapLimit
    ? (heapUsed / heapLimit) * 100
    : 0;

  return (
    <div>
      <h2>Memory Usage: {usagePercent.toFixed(1)}%</h2>
      <div className={`alert alert-${severity}`}>
        Severity: {severity}
      </div>
      <p>{formatted.heapUsed} / {formatted.heapLimit}</p>
    </div>
  );
}

Memory Snapshots

import { useMemoryMonitor } from "@usefy/use-memory-monitor";
import { useState } from "react";

function SnapshotComparison() {
  const {
    takeSnapshot,
    compareSnapshots,
    getAllSnapshots,
    clearSnapshots,
    formatted,
  } = useMemoryMonitor({ interval: 1000 });

  const [snapshots, setSnapshots] = useState<string[]>([]);

  const handleSnapshot = () => {
    const snapshot = takeSnapshot(`snapshot-${Date.now()}`);
    if (snapshot) {
      setSnapshots([...snapshots, snapshot.id]);
    }
  };

  const handleCompare = () => {
    if (snapshots.length >= 2) {
      const diff = compareSnapshots(snapshots[0], snapshots[snapshots.length - 1]);
      if (diff) {
        console.log("Memory difference:", {
          heapUsed: (diff.heapUsed / 1024 / 1024).toFixed(2) + " MB",
          heapTotal: (diff.heapTotal / 1024 / 1024).toFixed(2) + " MB",
          duration: (diff.duration / 1000).toFixed(1) + " seconds",
        });
      }
    }
  };

  return (
    <div>
      <h2>Snapshots ({snapshots.length})</h2>
      <p>Current: {formatted.heapUsed}</p>

      <button onClick={handleSnapshot}>Take Snapshot</button>
      <button onClick={handleCompare} disabled={snapshots.length < 2}>
        Compare First & Last
      </button>
      <button onClick={clearSnapshots}>Clear All</button>

      <ul>
        {getAllSnapshots().map((snapshot) => (
          <li key={snapshot.id}>
            {snapshot.id}: {(snapshot.data.heapUsed || 0) / 1024 / 1024} MB
          </li>
        ))}
      </ul>
    </div>
  );
}

History & Trend Analysis

import { useMemoryMonitor } from "@usefy/use-memory-monitor";

function HistoryTracker() {
  const { history, trend, formatted, clearHistory } = useMemoryMonitor({
    interval: 500,
    enableHistory: true,
    historySize: 30,
  });

  const maxHeapUsed = Math.max(...history.map((h) => h.heapUsed || 0), 1);

  return (
    <div>
      <h2>Memory History</h2>
      <p>Current: {formatted.heapUsed}</p>
      <p>
        Trend: {trend === "increasing" ? "↗" : trend === "decreasing" ? "↘" : "→"} {trend}
      </p>
      <p>History Points: {history.length}</p>

      <button onClick={clearHistory}>Clear History</button>

      {/* Simple bar chart */}
      <div style={{ display: "flex", alignItems: "flex-end", height: 100 }}>
        {history.map((point, i) => {
          const heightPercent = ((point.heapUsed || 0) / maxHeapUsed) * 100;
          return (
            <div
              key={i}
              style={{
                flex: 1,
                height: `${heightPercent}%`,
                backgroundColor: "blue",
                margin: "0 1px",
              }}
              title={`${((point.heapUsed || 0) / 1024 / 1024).toFixed(2)} MB`}
            />
          );
        })}
      </div>
    </div>
  );
}

Browser Support Detection

import { useMemoryMonitor } from "@usefy/use-memory-monitor";

function SupportDetection() {
  const { isSupported, getUnsupportedInfo, getBrowserSupport } = useMemoryMonitor();

  if (!isSupported) {
    const info = getUnsupportedInfo();
    return (
      <div>
        <h2>Limited Support</h2>
        <p>Reason: {info.reason}</p>
        <p>Browser: {info.browser || "Unknown"}</p>
        <p>Available fallbacks: {info.availableFallbacks.join(", ")}</p>
      </div>
    );
  }

  const support = getBrowserSupport();

  return (
    <div>
      <h2>Browser Support</h2>
      <p>Level: {support.level}</p>
      <p>Available metrics: {support.availableMetrics.join(", ")}</p>
      <p>Secure context: {support.isSecureContext ? "Yes" : "No"}</p>
      <p>Cross-origin isolated: {support.isCrossOriginIsolated ? "Yes" : "No"}</p>
      {support.limitations.length > 0 && (
        <ul>
          {support.limitations.map((limitation, i) => (
            <li key={i}>{limitation}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

Garbage Collection Request

The requestGC function provides a way to request garbage collection. Note that this is a hint only and is not guaranteed to trigger GC in all environments.

import { useMemoryMonitor } from "@usefy/use-memory-monitor";

function GCExample() {
  const { requestGC, formatted } = useMemoryMonitor({
    devMode: true,
    logToConsole: true, // Enable logging to see GC status
  });

  return (
    <div>
      <p>Heap Used: {formatted.heapUsed}</p>
      <button onClick={requestGC}>Request GC</button>
    </div>
  );
}

How it works:

  • If globalThis.gc() is available (Chrome with --expose-gc flag or Node.js with --expose-gc), it will directly trigger garbage collection
  • Otherwise, it creates temporary memory pressure as a hint to the JS engine
  • With devMode and logToConsole enabled, you'll see console logs indicating whether GC was triggered or just hinted

To enable direct GC in Chrome:

# Windows
chrome.exe --js-flags="--expose-gc"

# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --js-flags="--expose-gc"

# Linux
google-chrome --js-flags="--expose-gc"

Performance

Optimizations

  1. Tab Visibility: Automatically pauses monitoring when tab is hidden (configurable with pauseWhenHidden)
  2. Circular Buffer: Uses efficient circular buffer for history storage (O(1) operations)
  3. Stable References: All callback functions are memoized with useCallback
  4. useSyncExternalStore: React 18+ concurrent mode safe state management
  5. Minimal Re-renders: Only triggers re-renders when actual memory data changes

Bundle Size

The hook is lightweight and tree-shakeable:

  • Main hook: ~8KB minified
  • With all utilities: ~15KB minified
  • Gzipped: ~5KB

TypeScript

This hook is written in TypeScript and exports comprehensive type definitions:

import {
  useMemoryMonitor,
  type UseMemoryMonitorOptions,
  type UseMemoryMonitorReturn,
  type MemoryInfo,
  type MemorySnapshot,
  type LeakAnalysis,
  type BrowserSupport,
  type Severity,
  type Trend,
} from "@usefy/use-memory-monitor";

// Full type safety
const options: UseMemoryMonitorOptions = {
  interval: 1000,
  autoStart: true,
};

const monitor: UseMemoryMonitorReturn = useMemoryMonitor(options);

Testing

This package maintains comprehensive test coverage to ensure reliability and stability.

Test Coverage

📊 View Detailed Coverage Report (GitHub Pages)

Test Categories

225 tests passed covering:

  • Initialization & Lifecycle: Mount, unmount, start/stop behavior
  • Memory Tracking: Polling, history, trend analysis
  • Leak Detection: Linear regression, sensitivity levels, analysis
  • Thresholds: Severity calculation, callbacks
  • Snapshots: Create, compare, clear operations
  • Browser Detection: API availability, fallback strategies
  • Edge Cases: SSR, unsupported browsers, invalid inputs
  • Store Management: State updates, subscribers, batch operations
  • Memory APIs: Performance.memory, DOM nodes, event listeners

License

MIT © mirunamu

This package is part of the usefy monorepo.