Reading Device Battery Level with a Custom Capacitor Native Plugin
Learn how to expose iOS and Android battery APIs through a Capacitor plugin so your web code can read the charge percentage with a single JavaScript call.
15 Dec 2025, 13:38 UTC

Problem: Getting reliable battery information from a Capacitor app
When building a cross‑platform mobile app with Capacitor, you often need to show the current battery charge or react to low‑power states. The web layer cannot directly access hardware, so you must bridge to native code. Without a plugin, you would have to maintain separate iOS and Android projects or resort to unreliable work‑arounds.
Thesis: A small, typed Capacitor plugin lets you call a native battery method as if it were a regular JavaScript function, keeping the rest of your codebase unchanged.
Implementation Overview
The plugin consists of three parts:
- A Capacitor‑compatible class annotated with
@Pluginthat exposes a@Methodreturning the charge percentage. - Platform‑specific code that reads the battery level using
UIDevice(iOS) orBatteryManager(Android). - Registration in
capacitor.config.jsonso the CLI can sync the plugin into the native projects.
Worked Example: iOS and Android Source
iOS (Swift)
import Capacitor
@Plugin("BatteryPlugin")
public class BatteryPlugin: CAPPlugin {
@objc func getLevel(_ call: CAPPluginCall) {
let level = UIDevice.current.batteryLevel // -1.0 if unknown
if level < 0 {
call.reject("Battery level not available")
return
}
let percent = Int(level * 100)
call.resolve(["value": percent])
}
}
Android (Java)
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import android.content.Context;
import android.os.BatteryManager;
import android.os.Build;
@CapacitorPlugin(name = "BatteryPlugin")
public class BatteryPlugin extends Plugin {
@PluginMethod
public void getLevel(PluginCall call) {
Context ctx = getContext();
int level = -1;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
level = ctx.getSystemService(Context.BATTERY_SERVICE)
.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = ctx.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
level = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
if (level < 0) {
call.reject("Battery level not available");
} else {
call.resolve(new JSObject().put("value", level));
}
}
}
Plugin Registration
Add an entry to capacitor.config.json (create the file if it does not exist):
{
"plugins": {
"BatteryPlugin": {
"ios": {
"src": "ios/App/BatteryPlugin.swift"
},
"android": {
"src": "src/main/java/com/example/battery/BatteryPlugin.java"
}
}
}
}
Run npx cap sync to copy the source into the native projects and rebuild.
Using the Plugin from Web Code
After syncing, import the plugin wherever you need it:
import { Plugins } from '@capacitor/core';
const { BatteryPlugin } = Plugins;
async function showBattery() {
const ret = await BatteryPlugin.getLevel();
document.getElementById('battery').textContent = ret.value + '%';
}
// Example: call on button click
document.getElementById('btn').addEventListener('click', showBattery);
The call returns a promise that resolves to an object { value: number } representing the charge percentage (0‑100).
Trade‑offs and Limitations
- Version coupling: The plugin relies on Capacitor’s plugin API. Major Capacitor releases (e.g., moving from 5.x to 6.x) may change the
@Pluginor@Methodannotations, requiring updates to the native class. - Simulator/emulator behavior: iOS simulators always report a battery level of -1.0 (unknown) unless you manually set it via Hardware → Battery. Android emulators often return 100 %. Therefore, validate the plugin on a physical device to see realistic values.
- Platform differences: Android versions prior to Lollipop require a sticky broadcast receiver, which adds a few lines of code. If you target only newer Android versions, you can simplify the implementation.
Actionable Closing
To verify that the plugin is wired correctly:
- Build and run the app on a real iOS or Android device.
- Trigger the
showBatteryfunction (e.g., by pressing a button). - Compare the displayed percentage with the system battery indicator shown in the device’s status bar.
- If the values match within a few percent, the plugin is functioning; otherwise, revisit the native implementation and ensure the plugin appears in
capacitor.config.json.
With this pattern you can expose any other native capability—such as Bluetooth, sensors, or filesystem access—while keeping a single JavaScript interface across platforms.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.