January 15, 2025 • 18 min read
Battery and CPU are a UX cost too. Here's a repeatable way to profile and trim the waste.
We tend to treat energy as someone else's problem. But a chatty React app drains a phone battery and spins up fans on a laptop, and that's a UX cost the same way a slow load is. Over a few projects I've ended up running the same six steps to find where the waste is and trim it. In the best cases that's cut CPU work by around 60%, and the app felt faster afterwards too.
Don't guess. Before you change anything, find out where the work is actually going. The React DevTools Profiler is enough to start:
// Energy profiling with React DevTools Profiler
import { Profiler } from 'react';
function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
console.log('Component:', id);
console.log('Phase:', phase);
console.log('Actual Duration:', actualDuration);
console.log('Base Duration:', baseDuration);
// Log high-energy components
if (actualDuration > 16) {
console.warn('High-energy component detected:', id, actualDuration + 'ms');
}
}
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<Header />
<MainContent />
<Footer />
</Profiler>
);
}Leaks don't announce themselves. They just quietly keep the CPU busy. Here's a small hook I use to flag components that re-render far more than they should:
// Memory leak detection and prevention
import { useEffect, useRef } from 'react';
function useMemoryLeakDetection(componentName) {
const renderCount = useRef(0);
useEffect(() => {
renderCount.current++;
// Detect excessive re-renders
if (renderCount.current > 10) {
console.warn(`${componentName} has rendered ${renderCount.current} times`);
}
});
}On real apps these steps have landed somewhere in the 40-60% range for energy, and nothing here made the UX worse. The order matters though: profile first so you're fixing the thing that's actually expensive, not the thing you assume is. And remember the device on the other end isn't your dev machine.