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-performance-toolkit

v0.3.1

Published

react-native-performance-toolkit is a react native package built with Nitro

Downloads

31,726

Readme

React Native Performance Toolkit

Low overhead monitoring of important performance metrics for React Native apps like JS FPS, UI FPS, CPU usage, and memory usage.

Installation

bun add react-native-performance-toolkit react-native-nitro-modules

Optional: Reanimated support

If you want to use UI thread components and hooks (recommended for accurate FPS display), install the optional dependencies:

bun add react-native-reanimated react-native-worklets react-native-gesture-handler

Requirements

  • React Native v0.76.0 or higher
  • (Optional) Reanimated v4 or higher - for UI thread components and hooks

Usage

Very simple usage - get value once

import {
  getJsFps,
  getUiFps,
  getCpuUsage,
  getMemoryUsage,
  getDeviceMaxRefreshRate,
  getDeviceCurrentRefreshRate,
} from 'react-native-performance-toolkit'

console.log('JS FPS:', getJsFps())
console.log('UI FPS:', getUiFps())
console.log('CPU Usage:', getCpuUsage())
console.log('Memory Usage:', getMemoryUsage())
console.log('Max Refresh Rate:', getDeviceMaxRefreshRate(), 'Hz')
console.log('Current Refresh Rate:', getDeviceCurrentRefreshRate(), 'Hz')

Subscribe to changes

import { onFpsJsChange } from 'react-native-performance-toolkit'

const unsubscribe = onFpsJsChange((fps) => {
  console.log('JS FPS changed:', fps)
})

// later
unsubscribe()

React Hooks - JS Thread only

Please be aware that this hook will not update if your JS thread is blocked (0 FPS) because updates are happening only on that very same thread.

import { useFpsJs } from 'react-native-performance-toolkit'

const SomeComponent = () => {
  const jsFps = useFpsJs()
  return <Text>JS FPS: {jsFps}</Text>
}

Reanimated Hooks - UI Thread

Note: These features require react-native-reanimated and react-native-worklets to be installed. Import from react-native-performance-toolkit/reanimated.

To avoid the issue with not showing 0 FPS, it's recommended to use Reanimated based hooks or pre-made components. This will ensure the value is updated even if the JS thread is blocked.

import { TextInput } from 'react-native'
import { useFpsJsSharedValue } from 'react-native-performance-toolkit/reanimated'
import Animated, {
  useAnimatedReaction,
  useAnimatedRef,
  setNativeProps,
} from 'react-native-reanimated'

const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)

const SomeComponent = () => {
  const inputRef = useAnimatedRef<TextInput>()
  const jsFps = useFpsJsSharedValue()

  useAnimatedReaction(
    () => jsFps.value.toString(),
    (value) => {
      setNativeProps(inputRef, { text: value })
    }
  )

  return <AnimatedTextInput ref={inputRef} />
}

Pre-made Reanimated components - UI Thread

Note: These features require react-native-reanimated, react-native-worklets, and react-native-gesture-handler to be installed. Import from react-native-performance-toolkit/reanimated.

For better DX, the library provides pre-made Reanimated components that run solely on the UI thread. You can use either the convenience wrappers or the flexible base component:

import {
  JSFpsCounter,
  UIFpsCounter,
  CpuUsageCounter,
  MemoryUsageCounter,
  UIThreadReanimatedCounter,
} from 'react-native-performance-toolkit/reanimated'

const SomeComponent = () => {
  return (
    <>
      {/* Convenience components */}
      <JSFpsCounter />
      <UIFpsCounter />
      <CpuUsageCounter />
      <MemoryUsageCounter />

      {/* Or use the flexible base component with custom labels */}
      <UIThreadReanimatedCounter label="JS FPS" type="js" />
      <UIThreadReanimatedCounter label="UI FPS" type="ui" />
      <UIThreadReanimatedCounter label="CPU" type="cpu" />
      <UIThreadReanimatedCounter label="RAM" type="memory" />
    </>
  )
}

Direct buffer access (advanced usage, experimental)

Some advanced usage might require direct buffer access. For example, you might want to use this library in a custom native component or you might want to use it in a worklet thread. This is experimental and might be changed in the future.

import {
  getJsFpsBuffer,
  getUiFpsBuffer,
  getCpuUsageBuffer,
  getMemoryUsageBuffer,
} from 'react-native-performance-toolkit'

const jsFpsBuffer = getJsFpsBuffer()
const uiFpsBuffer = getUiFpsBuffer()
const cpuUsageBuffer = getCpuUsageBuffer()
const memoryUsageBuffer = getMemoryUsageBuffer()

const getValueFromBuffer = (buffer: ArrayBuffer) => {
  const view = new DataView(buffer)
  return view.getInt32(0, true) // true = littleEndian
}

console.log('JS FPS:', getValueFromBuffer(jsFpsBuffer))
console.log('UI FPS:', getValueFromBuffer(uiFpsBuffer))
console.log('CPU Usage:', getValueFromBuffer(cpuUsageBuffer))
console.log('Memory Usage:', getValueFromBuffer(memoryUsageBuffer))

Access from worklets (advanced usage)

Note: This requires react-native-reanimated and react-native-worklets to be installed.

You can also access the value from any worklet thread, but to do that you need to use Nitro Modules unboxing function. For more detailed implementation look for source code of UI Reanimated hooks like useFpsJsSharedValue.

import { useCallback } from 'react'
import {
  BoxedJsFpsTracking,
  BoxedPerformanceToolkit,
} from 'react-native-performance-toolkit'

// ...

const updateFps = useCallback(() => {
  'worklet'
  const unboxedJsFps = BoxedJsFpsTracking.unbox()
  const unboxedPerformanceToolkit = BoxedPerformanceToolkit.unbox()

  let buffer = unboxedJsFps.getJsFpsBuffer()

  const view = new DataView(buffer)
  const value = view.getInt32(0, true)

  console.log('JS FPS:', value)

  // update shared value for example
  fpsValue.value = value
}, [])

API Reference

Core API (no additional dependencies)

  • Simple getters

    • getJsFps(): number - Returns current JS FPS (0-60)
    • getUiFps(): number - Returns current UI FPS (0-30/60/90/120/...)
    • getCpuUsage(): number - Returns CPU usage percentage in Linux format
    • getMemoryUsage(): number - Returns memory usage in bytes
    • getDeviceMaxRefreshRate(): number - Returns device's maximum supported refresh rate (e.g., 120 Hz on ProMotion devices)
    • getDeviceCurrentRefreshRate(): number - Returns device's current active refresh rate (may be lower than max on adaptive refresh rate displays)
  • Subscription functions

    • onFpsJsChange(callback: (fps: number) => void): () => void - Subscribe to JS FPS changes
    • onFpsUiChange(callback: (fps: number) => void): () => void - Subscribe to UI FPS changes
    • onCpuChange(callback: (value: number) => void): () => void - Subscribe to CPU usage changes
    • onMemoryChange(callback: (value: number) => void): () => void - Subscribe to memory usage changes
  • React Hooks (JS Thread)

    • useFpsJs(): number - Hook that returns current JS FPS
    • useFpsUi(): number - Hook that returns current UI FPS
    • useCpuUsage(): number - Hook that returns current CPU usage
    • useMemoryUsage(): number - Hook that returns current memory usage
  • Buffer-based API

    • getJsFpsBuffer(): ArrayBuffer - Returns ArrayBuffer with JS FPS data
    • getUiFpsBuffer(): ArrayBuffer - Returns ArrayBuffer with UI FPS data
    • getCpuUsageBuffer(): ArrayBuffer - Returns ArrayBuffer with CPU usage data
    • getMemoryUsageBuffer(): ArrayBuffer - Returns ArrayBuffer with memory usage data
  • Advanced (Nitro Modules)

    • BoxedJsFpsTracking - Direct boxed Nitro module instance for worklet usage
      • getJsFpsBuffer(): ArrayBuffer
    • BoxedPerformanceToolkit - Direct boxed Nitro module instance for worklet usage
      • getUiFpsBuffer(): ArrayBuffer
      • getCpuUsageBuffer(): ArrayBuffer
      • getMemoryUsageBuffer(): ArrayBuffer
      • getDeviceMaxRefreshRate(): number
      • getDeviceCurrentRefreshRate(): number

Reanimated API (requires optional dependencies)

Import from react-native-performance-toolkit/reanimated:

  • React Components (runs on UI Thread)

    • <JSFpsCounter /> - Pre-made component displaying JS FPS
    • <UIFpsCounter /> - Pre-made component displaying UI FPS
    • <CpuUsageCounter /> - Pre-made component displaying CPU usage
    • <MemoryUsageCounter /> - Pre-made component displaying memory usage
    • <UIThreadReanimatedCounter label="..." type="js|ui|cpu|memory" /> - Flexible base component
    • <DraggableView> - Draggable wrapper component
  • Reanimated Hooks (UI Thread)

    • useFpsJsSharedValue() - Returns SharedValue with JS FPS
    • useFpsUiSharedValue() - Returns SharedValue with UI FPS
    • useFpsCpuSharedValue() - Returns SharedValue with CPU usage
    • useFpsMemorySharedValue() - Returns SharedValue with memory usage
    • useCounterSharedValue(type) - Generic hook for any counter type

Architecture

Low overhead tracking

On Android, the library is reading values from virtual files like /proc/stat for CPU usage and /proc/smaps_rollup for memory usage. This is very low overhead and doesn't require any additional permissions.

On iOS, the library is reading values from task_vm_info/rusage direct kernel call. This is also extremely low overhead.

Device Refresh Rate

The library provides two methods for getting refresh rate information:

  • Max Refresh Rate: Returns the maximum supported refresh rate of the device

    • Android: Uses Display.supportedModes to find the highest available refresh rate
    • iOS: Uses UIScreen.main.maximumFramesPerSecond
  • Current Refresh Rate: Returns the currently active refresh rate

    • Android: Uses Display.getRefreshRate() - useful for devices with adaptive refresh rate (e.g., 60/90/120Hz switching)
    • iOS: Uses CADisplayLink.preferredFramesPerSecond - useful for ProMotion displays that dynamically adjust

JS FPS Tracking

You can build JS FPS tracking in plain JS using setTimeout or requestAnimationFrame. But this library is running similar logic in C++ on the same thread as JS, with lower overhead and a few other benefits. For example, we can easily access the value from other threads.

For now, the FPS baseline for JS FPS is always 60 FPS as it doesn't reflect real UI updates but more like "how busy the JS thread is". I am not 100% sure if this is the correct approach and might change it in the future and I am open to suggestions.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.