Skip to content

Repository files navigation

⚡ react-native-energy-aware

React Native hooks that adapt your app to battery, thermal, and power save state — automatically.

Stop draining your users' battery. Stop ignoring thermal warnings. One hook, zero boilerplate.

npm version MIT License Platform iOS Platform Android


Why?

Most apps ignore the device energy state entirely. They animate at 60 FPS on a 5% battery, run heavy syncs when the device is overheating, and poll every 5 seconds regardless of power save mode.

react-native-energy-aware gives you a simple API to make your app a good energy citizen — without having to deal with UIDevice, NSProcessInfo, PowerManager, or BroadcastReceiver yourself.


Installation

npm install react-native-energy-aware
# or
yarn add react-native-energy-aware

iOS — run pod install after installing:

cd ios && pod install

Requires React Native 0.75+. Supports both New Architecture (TurboModule) and Old Architecture natively.


Quick start

import { useEnergyState } from 'react-native-energy-aware';

export function MyScreen() {
  const { energyMode, batteryLevel, thermalState } = useEnergyState();

  if (energyMode === 'critical') {
    return <LightweightView />;
  }

  return <FullFeaturedView />;
}

That's it. energyMode is a single computed value that combines battery level, low power mode, and thermal state — so you don't have to.


API

useEnergyState() — the main hook

Returns the full energy state of the device, updated in real time.

const {
  batteryLevel,    // number — 0.0 to 1.0 (-1 if unknown)
  batteryState,    // 'charging' | 'unplugged' | 'full' | 'unknown'
  isLowPowerMode,  // boolean
  thermalState,    // 'nominal' | 'fair' | 'serious' | 'critical'
  energyMode,      // 'normal' | 'saver' | 'critical' | 'overheating'
} = useEnergyState();

energyMode logic

Mode Triggers
overheating thermalState is serious or critical
critical battery < 15%, or (low power + battery < 20%) — not charging
saver low power mode on, or thermalState fair, or battery < 30%
normal everything else

Derived hooks

For most use cases, you don't even need energyMode — just use the purpose-built hooks:

useShouldReduceAnimations()

import { useShouldReduceAnimations } from 'react-native-energy-aware';

function MyComponent() {
  const reduceAnimations = useShouldReduceAnimations();

  return reduceAnimations
    ? <StaticBanner />
    : <AnimatedBanner />;
}

Returns true in saver, critical, or overheating mode.

useShouldReduceQuality()

import { useShouldReduceQuality } from 'react-native-energy-aware';

function Feed() {
  const reduceQuality = useShouldReduceQuality();

  return (
    <Image
      source={{ uri: reduceQuality ? thumbnailUrl : hdUrl }}
    />
  );
}

Returns true in critical or overheating mode.

useRecommendedFPS()

import { useRecommendedFPS } from 'react-native-energy-aware';

function GameLoop() {
  const fps = useRecommendedFPS(); // 60 | 30 | 15

  useEffect(() => {
    startLoop({ targetFPS: fps });
  }, [fps]);
}
Mode FPS
normal 60
saver 30
critical / overheating 15

useCanRunExpensiveTask()

import { useCanRunExpensiveTask } from 'react-native-energy-aware';

function SyncButton() {
  const canSync = useCanRunExpensiveTask();

  return (
    <Button
      onPress={canSync ? runHeavySync : showDeferredMessage}
      title={canSync ? 'Sync now' : 'Sync deferred (low battery)'}
    />
  );
}

Returns true only in normal mode. Use this to gate ML inference, large uploads, image processing, etc.

useAdaptiveInterval(config)

import { useAdaptiveInterval } from 'react-native-energy-aware';

function Feed() {
  const interval = useAdaptiveInterval({
    normal: 5000,   // poll every 5s normally
    saver: 30000,   // 30s in power save mode
    critical: 60000, // 60s when battery is critical
  });

  useEffect(() => {
    const id = setInterval(fetchData, interval);
    return () => clearInterval(id);
  }, [interval]);
}

Returns the appropriate interval in milliseconds based on energyMode. overheating uses the critical value.


Components

<EnergyAwareAnimation>

Renders children in normal mode, fallback when animations should be reduced. Works with any animation library — Lottie, Reanimated, Skia, etc.

import { EnergyAwareAnimation } from 'react-native-energy-aware';

<EnergyAwareAnimation fallback={<StaticBanner />}>
  <LottieView source={animation} autoPlay loop />
</EnergyAwareAnimation>

<EnergyDebugOverlay>

Debug overlay that displays real-time energy state. Wrap it in __DEV__ so it never ships to production.

import { EnergyDebugOverlay } from 'react-native-energy-aware';

// In your root component:
{__DEV__ && <EnergyDebugOverlay position="bottom-right" />}

Props: position'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' (default: 'bottom-right')


Real-world example

import {
  useEnergyState,
  useRecommendedFPS,
  useShouldReduceAnimations,
  useCanRunExpensiveTask,
} from 'react-native-energy-aware';

export function VideoFeed() {
  const { energyMode, batteryLevel } = useEnergyState();
  const fps = useRecommendedFPS();
  const reduceAnimations = useShouldReduceAnimations();
  const canRunHeavy = useCanRunExpensiveTask();

  return (
    <View>
      {energyMode === 'overheating' && (
        <Banner message="⚠️ Device is hot — quality reduced" />
      )}

      <VideoPlayer
        targetFPS={fps}
        quality={energyMode === 'normal' ? 'high' : 'low'}
        autoplay={energyMode !== 'critical'}
      />

      {reduceAnimations
        ? <StaticRecommendations />
        : <AnimatedRecommendations />}

      {canRunHeavy && (
        <Button onPress={runMLRecommendations} title="Get AI picks" />
      )}

      <Text>{Math.round(batteryLevel * 100)}% battery</Text>
    </View>
  );
}

Platform support

Feature iOS Android
Battery level UIDevice.batteryLevel BatteryManager
Battery state UIDevice.batteryState ACTION_BATTERY_CHANGED
Low power mode NSProcessInfo.isLowPowerModeEnabled PowerManager.isPowerSaveMode
Thermal state NSProcessInfo.thermalState PowerManager.currentThermalStatus (API 29+)
Real-time updates

On Android < API 29, thermalState always returns 'nominal' — there is no thermal API on older versions.


Architecture

Built on the New Architecture (TurboModule + Codegen) with native Old Architecture support (RCT_EXPORT_MODULE / ReactContextBaseJavaModule).

  • Native observers are ref-counted — multiple useEnergyState instances share a single native subscription.
  • No context or provider required for v0.1 — hooks are self-contained.

Roadmap

  • v1.0<EnergyProvider> context, EnergyAwareImage, EnergyAwareVideo, deferred task runner, plugin system for Reanimated / FlashList / Vision Camera / TanStack Query
  • v1.1+ — Energy budget tracker, background task throttling, carbon-aware scheduling

Contributing

License

MIT © ClemDelb

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages