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-native-frame-metrics

v0.0.1

Published

Native frame timing for React Native — dropped frames and UI-thread FPS via a TurboModule, with independent JS-thread instrumentation. Pre-release: not yet functional.

Readme

react-native-frame-metrics

Native frame timing for React Native — dropped frames and UI-thread FPS via a TurboModule, with independent JS-thread instrumentation.

Status: pre-release. The API below is the design target, not yet a shipped implementation. See Status for what currently works.


Why this exists

When a React Native screen feels janky, "the app dropped frames" is not a diagnosis. Frames get dropped for two unrelated reasons, and the fix is different in each case:

  • UI thread drops — expensive native layout, measurement, or draw work. The platform can't produce a frame in its 16.6ms budget.
  • JS thread drops — expensive JavaScript blocking the runtime, so state updates and animations starve even when the UI thread is idle.

Most profiling tooling reports a single blended frame rate, which tells you that something is slow but not where to look. This library measures both independently, so the number you get points at the layer that actually needs fixing.

How this differs from react-native-performance

react-native-performance is a mature, actively maintained library and the right tool for a different job. It implements a Performance API surface for React Native — mark(), measure(), resource timing, and native timeline events like app startup and script execution time. It answers "how long did this discrete thing take?"

This library samples continuous per-frame timing and separates the two threads. It answers "is this screen dropping frames right now, and which thread is responsible?"

They're complementary. If you want startup and navigation timings, use react-native-performance. If you want to profile scroll jank on a specific list, use this.

Architecture

┌─────────────────────────────────────────────────────────┐
│  JavaScript                                             │
│                                                         │
│  ┌──────────────────────┐   ┌────────────────────────┐  │
│  │ JS-thread sampler    │   │ Typed JS API + overlay │  │
│  │ (rAF loop, measures  │   │                        │  │
│  │  JS runtime stalls)  │   │                        │  │
│  └──────────────────────┘   └───────────┬────────────┘  │
└───────────────────────────────────────── │ ─────────────┘
                                           │
                              Codegen-generated bindings
                                           │
┌───────────────────────────────────────── │ ─────────────┐
│  TurboModule (native)                    ▼              │
│                                                         │
│  ┌────────────────────────┐  ┌────────────────────────┐ │
│  │ Android — Kotlin       │  │ iOS — Objective-C++    │ │
│  │ Choreographer          │  │ CADisplayLink on the   │ │
│  │ .FrameCallback         │  │ main run loop          │ │
│  └────────────────────────┘  └────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

Design notes

Drops are inferred from timestamp gaps, not counted from missed callbacks. Both Choreographer.FrameCallback and CADisplayLink are driven by the main thread. When that thread is blocked — exactly the condition worth measuring — the callback doesn't fire at all. So a dropped frame can't be observed directly; it's derived from the delta between consecutive callback timestamps against the display's refresh interval.

JS-thread rate needs a separate mechanism. It can't be read from the native frame callbacks, because those run on the UI thread. A JS-side loop measures runtime stalls independently, which is what makes the two-number output possible.

Refresh rate is not assumed to be 60Hz. The frame budget is derived from the display's actual refresh interval, so 90Hz and 120Hz devices report correctly rather than showing phantom drops.

Planned API

import { start, stop, subscribe } from 'react-native-frame-metrics';

start();

const unsubscribe = subscribe((sample) => {
  // {
  //   uiThreadFps: number
  //   jsThreadFps: number
  //   droppedFrames: number
  //   frameBudgetMs: number
  // }
});

A dev-mode overlay component is planned for on-device inspection without wiring up a listener.

Status

  • [ ] Android — Choreographer.FrameCallback sampling
  • [ ] Codegen spec and TurboModule bindings
  • [ ] iOS — CADisplayLink sampling
  • [ ] JS-thread instrumentation
  • [ ] Dev-mode overlay component
  • [ ] Example app with a jank-heavy list, profiled before and after optimization
  • [ ] Published to npm

Requirements

  • React Native 0.76+ (New Architecture required — this is a TurboModule and has no legacy bridge fallback)
  • Android and iOS

License

MIT © Ayush Bharadva