Using the Battery Status API in Web Apps
Most web apps have no idea whether the person using them is at 8% battery on a train with no charger in sight. The Battery Status API changes that — at least in some browsers. It lets your JavaScript read the device’s battery level, charging state, and estimated time remaining, so you can adapt your app’s behavior accordingly.
This article explains how the API works, what you can realistically do with it, and why you need to be careful about where and how you use it.
Key Takeaways
- The Battery Status API exposes battery level, charging state, and time estimates through
navigator.getBattery(), returning aBatteryManagerobject with four readable properties and four corresponding change events. - Always use feature detection and error handling before calling the API, since it is only available in secure contexts (HTTPS) and only in Chromium-based browsers.
- Firefox removed the API over fingerprinting concerns, and Safari never implemented it, so treat battery-aware behavior strictly as a progressive improvement — never as a core feature.
- Practical applications include pausing background sync, reducing animations, and prompting users to save work when battery is low.
What Is the Battery Status API?
The Battery Status API exposes battery information through the navigator.getBattery() method, which returns a Promise that resolves to a BatteryManager object. That object gives you four properties:
level— a number between0and1(e.g.,0.72means 72%)charging—trueif the device is plugged in,falseif notchargingTime— estimated seconds until fully chargeddischargingTime— estimated seconds until battery is empty
Both time properties can return Infinity — when the device is a desktop with no battery, when the estimate isn’t ready yet, or when the browser deliberately withholds precision to limit fingerprinting risk.
How to Use navigator.getBattery() Safely
Always check for support before calling the API. It is only available in secure contexts (HTTPS), and even then, only in browsers that expose it.
In embedded contexts, access can also be restricted by the battery Permissions Policy.
async function initBatteryMonitor() {
// Feature detection — don't assume the API exists
if (!('getBattery' in navigator)) {
console.warn('Battery Status API not supported in this browser.')
return
}
let battery
try {
battery = await navigator.getBattery()
} catch (err) {
console.error('Failed to access battery information:', err)
return
}
// Read initial values
console.log(`Level: ${battery.level * 100}%`)
console.log(`Charging: ${battery.charging}`)
// Handle Infinity gracefully before displaying
const formatTime = (seconds) =>
seconds === Infinity ? 'Unknown' : `${Math.round(seconds / 60)} min`
console.log(`Charging time: ${formatTime(battery.chargingTime)}`)
console.log(`Discharging time: ${formatTime(battery.dischargingTime)}`)
// Listen for changes
battery.addEventListener('levelchange', () => {
console.log(`Battery level updated: ${battery.level * 100}%`)
})
battery.addEventListener('chargingchange', () => {
console.log(`Charging state changed: ${battery.charging}`)
})
}
initBatteryMonitor()
The four events you can listen to are levelchange, chargingchange, chargingtimechange, and dischargingtimechange. Each fires when its corresponding value changes.
Practical Use Cases
The API is most useful when you want to reduce resource consumption for users on low battery:
- Pause background sync or polling when
battery.leveldrops below a threshold like0.2 - Reduce animation complexity or disable auto-playing video
- Prompt users to save their work before the battery runs out
- Adjust PWA behavior — for example, deferring non-critical network requests
These are progressive improvements. Your app should work fine without them.
Discover how at OpenReplay.com.
Browser Support and Privacy Concerns
This is where the Battery Status API gets complicated.
| Browser | Support | Notes |
|---|---|---|
| Chromium-based browsers | Supported with limited availability | HTTPS only; values may be reduced, rounded, or replaced with default values |
| Firefox | No | Removed due to privacy concerns |
| Safari | No | Never implemented |
Current browser support is narrow. Firefox removed the API because researchers demonstrated it could be used as a fingerprinting vector — the combination of battery level and time values was specific enough to help track users across sites. In response, Chromium-based browsers may reduce precision or expose default values to limit that risk.
This means you should never rely on the Battery API as a primary feature. Treat it as a nice-to-have improvement, and always write fallback behavior for when it returns nothing.
Testing the Battery API in Development
Chrome DevTools does not currently provide a built-in battery simulator under Sensors. To test battery-related behavior during development, you have a few practical options:
- Use a real mobile device connected via USB remote debugging, and observe actual battery state changes.
- Mock the API in your code by replacing
navigator.getBatterywith a function that returns a fakeBatteryManagerobject with the properties and events you need to test. - Use browser overrides or testing libraries such as Sinon or Jest to stub
navigator.getBattery()in your test suite.
Here is a minimal mock you can drop into your dev environment:
function mockBattery({ level = 0.15, charging = false } = {}) {
const fake = {
level,
charging,
chargingTime: charging ? 3600 : Infinity,
dischargingTime: charging ? Infinity : 1800,
addEventListener: () => {},
}
navigator.getBattery = () => Promise.resolve(fake)
}
// Simulate a low-battery, unplugged device
mockBattery({ level: 0.08, charging: false })
This approach gives you full control over the values your code receives, without depending on browser-specific tooling.
Conclusion
The Battery Status API is a niche tool with real limitations. Browser support is narrow, values are intentionally imprecise, and the privacy implications mean its future is uncertain. But in controlled environments — internal tools, PWAs targeting known Chromium-based browsers, or kiosk-style applications — it can meaningfully improve the user experience.
Use feature detection, handle Infinity values explicitly, clean up your event listeners when they’re no longer needed, and never build a critical feature on top of it.
FAQs
Only on Chromium-based mobile browsers such as Chrome for Android, and even there availability can vary by environment. Safari on iOS has never implemented it, and Firefox removed support entirely. Always use feature detection to check for availability before calling navigator.getBattery(), and provide fallback behavior for unsupported browsers.
It can be. Researchers showed that the combination of battery level and discharge time values was precise enough to fingerprint users across websites. This led Firefox to remove the API completely. Chromium-based browsers may reduce precision or expose default values to limit that risk, but the concern remains a reason some browsers avoid implementing it.
Store references to your handler functions and call removeEventListener when they are no longer needed, such as when a component unmounts. Since the BatteryManager object persists, failing to remove listeners can cause memory leaks or unexpected behavior if your app re-initializes the monitor multiple times.
It works in supported Chromium-based desktop browsers, but the values may not be meaningful. Desktops without batteries often report a level of 1 and a charging state of true, with both time properties set to Infinity. The API is most useful on laptops and mobile devices where battery state actually fluctuates.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.