<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lav Pranjale</title>
    <description>The latest articles on DEV Community by Lav Pranjale (@lav_pranjale_4cdd421d464d).</description>
    <link>https://dev.to/lav_pranjale_4cdd421d464d</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3407210%2Fd0b2a41b-3b6d-4ba6-9c64-6d0a1802a4a9.jpg</url>
      <title>DEV Community: Lav Pranjale</title>
      <link>https://dev.to/lav_pranjale_4cdd421d464d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lav_pranjale_4cdd421d464d"/>
    <language>en</language>
    <item>
      <title>🔌 Build a Native Module in React Native (Battery Level Example for Android &amp; iOS)</title>
      <dc:creator>Lav Pranjale</dc:creator>
      <pubDate>Sat, 02 Aug 2025 14:52:55 +0000</pubDate>
      <link>https://dev.to/lav_pranjale_4cdd421d464d/build-a-native-module-in-react-native-battery-level-example-for-android-ios-46i4</link>
      <guid>https://dev.to/lav_pranjale_4cdd421d464d/build-a-native-module-in-react-native-battery-level-example-for-android-ios-46i4</guid>
      <description>&lt;p&gt;Ever wonder how libraries like &lt;code&gt;react-native-camera&lt;/code&gt; or &lt;code&gt;react-native-device-info&lt;/code&gt; access native features?&lt;br&gt;
In this guide, you’ll learn how to create your own native module in React Native — using Kotlin for Android and Swift for iOS — to fetch the battery level of the device.&lt;br&gt;
You'll understand how the React Native bridge works and how JavaScript can talk directly to platform-native code.&lt;/p&gt;
&lt;h2&gt;
  
  
  🚀 What You’ll Build
&lt;/h2&gt;

&lt;p&gt;We'll build a lightweight but powerful native module called &lt;code&gt;BatteryStatus&lt;/code&gt; that retrieves the current battery level of the device.&lt;/p&gt;

&lt;p&gt;It’s a perfect hands-on project to understand how the React Native bridge acts as a translator between your JavaScript and native code — giving you the best of both worlds: cross-platform development and low-level access to device features.&lt;/p&gt;
&lt;h2&gt;
  
  
  ✅ Pre-requisites Checklist
&lt;/h2&gt;

&lt;p&gt;Make sure the following tools are installed and working:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Node.js &amp;amp; npm&lt;/li&gt;
&lt;li&gt;✅ React Native CLI (&lt;code&gt;npx react-native init&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;✅ Android Studio (for Android)&lt;/li&gt;
&lt;li&gt;✅ Xcode (for iOS on macOS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠️ This tutorial is for React Native CLI projects (not Expo). Expo doesn’t support custom native modules out of the box unless you eject or use Expo Modules API.&lt;/p&gt;
&lt;h3&gt;
  
  
  🏗️ Step 1: Create a New Project
&lt;/h3&gt;

&lt;p&gt;Start by initializing a fresh React Native app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx react-native init BatteryModuleApp
cd BatteryModuleApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll first create the native module for Android, then move on to iOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 Android: Build the Native Module in Kotlin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🧱 Step 2: Create the &lt;code&gt;BatteryModule.kt&lt;/code&gt; Class
&lt;/h3&gt;

&lt;p&gt;📄 &lt;code&gt;android/app/src/main/java/com/batterymoduleapp/BatteryModule.kt&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.batterymoduleapp

import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import com.facebook.react.bridge.*

class BatteryModule(reactContext: ReactApplicationContext) :
  ReactContextBaseJavaModule(reactContext) {

  // Module name used to access from JavaScript
  override fun getName(): String = "BatteryStatus"

  @ReactMethod
  fun getBatteryLevel(promise: Promise) {
    try {
      // Get battery info from system intent
      val batteryIntent = reactApplicationContext.registerReceiver(
        null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)
      )
      val level = batteryIntent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
      val scale = batteryIntent?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
      val batteryPct = level * 100 / scale

      promise.resolve(batteryPct) // Send value back to JS
    } catch (e: Exception) {
      promise.reject("BATTERY_ERROR", "Failed to get battery level", e)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📦 Step 3: Register the Module with &lt;code&gt;BatteryPackage.kt&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;📄 &lt;code&gt;android/app/src/main/java/com/batterymoduleapp/BatteryPackage.kt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.batterymoduleapp

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class BatteryPackage : ReactPackage {
  override fun createNativeModules(reactContext: ReactApplicationContext): List&amp;lt;NativeModule&amp;gt; {
    return listOf(BatteryModule(reactContext))
  }

  override fun createViewManagers(reactContext: ReactApplicationContext): List&amp;lt;ViewManager&amp;lt;*, *&amp;gt;&amp;gt; {
    return emptyList()
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧩 Step 4: Register the Package in &lt;code&gt;MainApplication.kt&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;📄&lt;code&gt;android/app/src/main/java/com/batterymoduleapp/MainApplication.kt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;override fun getPackages(): List&amp;lt;ReactPackage&amp;gt; {
  return listOf(
    MainReactPackage(),
    BatteryPackage() // 👈 Register your package here
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🍏 iOS: Build the Native Module in Swift
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🧱 Step 5: Create &lt;code&gt;BatteryStatus.swift&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;📄 &lt;code&gt;ios/BatteryModuleApp/BatteryStatus.swift&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Foundation
import UIKit

@objc(BatteryStatus)
class BatteryStatus: NSObject {

  @objc
  func getBatteryLevel(_ resolve: @escaping RCTPromiseResolveBlock,
                       rejecter reject: @escaping RCTPromiseRejectBlock) {
    UIDevice.current.isBatteryMonitoringEnabled = true
    let level = UIDevice.current.batteryLevel

    if level &amp;lt; 0 {
      reject("BATTERY_ERROR", "Battery level is unavailable", nil)
    } else {
      resolve(Int(level * 100))
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧠 What’s RCTPromiseResolveBlock?
&lt;/h3&gt;

&lt;p&gt;It’s a bridge-friendly way of returning asynchronous values from native Swift code back to JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧩 Step 6: Bridge Swift to React Native
&lt;/h3&gt;

&lt;p&gt;Create an Objective-C bridging file:&lt;/p&gt;

&lt;p&gt;📄 &lt;code&gt;ios/BatteryModuleApp/BatteryStatus.m&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#import &amp;lt;React/RCTBridgeModule.h&amp;gt;

@interface RCT_EXTERN_MODULE(BatteryStatus, NSObject)
RCT_EXTERN_METHOD(getBatteryLevel:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
@end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📄 If not already created, add a bridging header file:&lt;br&gt;
&lt;code&gt;BatteryModuleApp-Bridging-Header.h&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#import &amp;lt;React/RCTBridgeModule.h&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚛️ Step 7: Call the Module from JavaScript
&lt;/h3&gt;

&lt;p&gt;📄 &lt;code&gt;App.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import {NativeModules, Button, Alert, View} from 'react-native';

const {BatteryStatus} = NativeModules;

export default function App() {
  const getBattery = async () =&amp;gt; {
    try {
      const level = await BatteryStatus.getBatteryLevel();
      Alert.alert(`Battery Level: ${level}%`);
    } catch (e) {
      Alert.alert('Error fetching battery level');
    }
  };

  return (
    &amp;lt;View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}&amp;gt;
      &amp;lt;Button title="Get Battery Level" onPress={getBattery} /&amp;gt;
    &amp;lt;/View&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 This works because React Native’s bridge exposes our native &lt;code&gt;BatteryStatus&lt;/code&gt; class to JavaScript via &lt;code&gt;NativeModules&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Troubleshooting Tips
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;“Native module cannot be found” error?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android: Check if BatteryPackage is added to MainApplication.kt&lt;/li&gt;
&lt;li&gt;iOS: Confirm RCT_EXTERN_MODULE is in place and bridging header is set&lt;/li&gt;
&lt;li&gt;Try cleaning: cd android &amp;amp;&amp;amp; ./gradlew clean or npx react-native clean&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;iOS build fails on Swift?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure you’ve created and linked the Bridging-Header.h correctly&lt;/li&gt;
&lt;li&gt;Check your target’s build settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧠 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Custom native modules unlock the &lt;strong&gt;full capabilities&lt;/strong&gt; of React Native. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔓 &lt;strong&gt;Access low-level features&lt;/strong&gt; (camera, Bluetooth, sensors)&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Improve performance&lt;/strong&gt; by offloading heavy tasks to native code&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;Integrate native SDKs&lt;/strong&gt; (payments, health data, media)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And best of all — you gain &lt;strong&gt;true cross-platform control&lt;/strong&gt; while keeping the power of React Native’s declarative UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow me&lt;/strong&gt; on &lt;a href="https://www.linkedin.com/in/lav-pranjale-628559147/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Got questions or ideas for improvements? Drop them in the comments! 💬&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>javascript</category>
      <category>android</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
