Native Module no React Native — o que é, como criar e integrar

Autor: IT Sectr Publicado: 2026-06-03 Tempo de leitura: 9 min

Native Module é uma classe Java ou Objective-C que torna as APIs nativas da plataforma acessíveis a partir do JavaScript no React Native. Cada módulo é registrado no Bridge e exporta métodos que podem ser chamados do código JS como funções comuns. De acordo com Meta, 2024, o Native Module continua sendo a principal forma de integrar código de plataforma em aplicações React Native.

Pontos principais

  • Native Module — uma ponte entre JavaScript e o código nativo iOS ou Android.
  • Registro — o módulo é declarado através de anotações iOS (RCT_EXPORT_MODULE) ou Android (@ReactMethod).
  • Tipos de dados — primitivos, strings, arrays, dicionários e Promise são suportados.
  • Uso — do JS o módulo é acessado via NativeModules.NomeDoMódulo.
  • Nova arquitetura — no React Native 0.76+ o Native Module pode funcionar como Turbo Module via JSI.

O que é um Native Module?

Native Module é um elemento arquitetônico do React Native que permite executar código na linguagem da plataforma (Objective-C/Swift para iOS, Java/Kotlin para Android) e retornar o resultado para JavaScript. Sem um Native Module é impossível acessar as capacidades nativas do dispositivo — câmera, GPS, acelerômetro, sistema de arquivos ou Bluetooth.

O React Native vem com um conjunto de Native Modules integrados: CameraRoll, AsyncStorage, Geolocation, NetInfo e outros. No entanto, para tarefas específicas — integração de SDKs de terceiros, trabalho com sensores de hardware ou processos em segundo plano — o desenvolvedor cria módulos personalizados. De acordo com a pesquisa State of React Native 2024, 67% dos desenvolvedores usam pelo menos um Native Module personalizado em seus projetos.

A arquitetura do Native Module depende da versão do React Native. Na arquitetura clássica (React Native 0.72 e anteriores) o módulo se conecta através do Bridge e se comunica com JS de forma assíncrona via serialização JSON. Na nova arquitetura (React Native 0.76+) o módulo pode funcionar como Turbo Module, usando JSI para acesso síncrono sem serialização.

Criação de um Native Module para iOS

A criação de um Native Module para iOS começa com a declaração de uma classe Objective-C que implementa o protocolo RCTBridgeModule. A macro RCT_EXPORT_MODULE registra o módulo no Bridge, e RCT_EXPORT_METHOD exporta um método disponível a partir do JavaScript.

objective-c
// ImageCompressor.m — Native Module para iOS
@interface ImageCompressor () RCT_EXPORT_MODULE()
@end

@implementation ImageCompressor

RCT_EXPORT_METHOD(compressImage:(NSString *)imagePath
                  quality:(NSNumber *)quality
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    NSData *compressedData = [UIImageJPEGRepresentation(image, quality.floatValue)];
    NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"compressed.jpg"];
    [compressedData writeToFile:outputPath atomically:YES];
    resolve(outputPath);
}

@end

O método compressImage recebe um caminho de imagem e qualidade de compressão (0.0–1.0), processa os dados no lado nativo e retorna o caminho para o arquivo comprimido. A vantagem principal é que a compressão é realizada por código nativo, que é significativamente mais rápido e eficiente em memória do que a operação equivalente em JavaScript.

Para módulos Swift, a anotação @objc é usada antes da classe e dos métodos para torná-los disponíveis para o runtime Objective-C com o qual o Bridge trabalha. A classe deve herdar de NSObject e implementar RCTBridgeModule.

swift
// ImageCompressor.swift — Módulo Swift Nativo
@objc(ImageCompressor)
class ImageCompressor: NSObject {

    @objc
    func compressImage(
        _ imagePath: String,
        quality: Float,
        resolver: @escaping RCTPromiseResolveBlock,
        rejecter: @escaping RCTPromiseRejectBlock
    ) {
        guard let image = UIImage(contentsOfFile: imagePath) else {
            rejecter("FILE_ERROR", "Cannot load image", nil)
            return
        }
        guard let data = image.jpegData(compressionQuality: CGFloat(quality)) else {
            rejecter("COMPRESS_ERROR", "Compression failed", nil)
            return
        }
        let outputPath = NSTemporaryDirectory() + "compressed.jpg"
        try? data.write(to: URL(fileURLWithPath: outputPath))
        resolver(outputPath)
    }
}

Criação de um Native Module para Android

No Android, um Native Module é criado como uma classe Java que estende ReactContextBaseJavaModule. A anotação @ReactMethod exporta o método para o Bridge. A interface Promise de com.facebook.react.bridge é usada para retornar resultados.

java
// ImageCompressorModule.java — Native Module para Android
public class ImageCompressorModule
    extends ReactContextBaseJavaModule {

    @Override
    public String getName() {
        return "ImageCompressor";
    }

    @ReactMethod
    public void compressImage(
            String imagePath,
            Float quality,
            Promise promise) {
        try {
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            File outputFile = new File(
                ReactNative.getApplicationContext()
                    .getCacheDir(), "compressed.jpg");
            FileOutputStream fos = new FileOutputStream(outputFile);
            bitmap.compress(
                Bitmap.CompressFormat.JPEG,
                (int)(quality * 100), fos);
            fos.close();
            promise.resolve(outputFile.getAbsolutePath());
        } catch (Exception e) {
            promise.reject("COMPRESS_ERROR", e.getMessage());
        }
    }
}

O método getName() retorna o nome do módulo sob o qual ele será acessível a partir do JavaScript. No exemplo acima, o módulo é registrado como ImageCompressor. A anotação @ReactMethod indica ao Bridge que o método deve ser exportado. Importante: os métodos devem ser void e aceitar apenas tipos suportados pelo Bridge: String, Boolean, Integer, Double, ReadableArray, ReadableMap, Promise.

Após criar a classe do módulo, ele deve ser registrado no pacote da aplicação. Para isso, uma classe que implementa ReactPackage é criada e adicionada à lista de módulos no método createNativeModules.

java
// ImageCompressorPackage.java — registro do módulo
public class ImageCompressorPackage implements ReactPackage {

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        return Arrays.asList(
            new ImageCompressorModule(reactContext)
        );
    }

    @Override
    public List<ViewManager> createViewManagers(
            ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

Registro e uso de um Native Module

Após criar os módulos para ambas as plataformas, eles devem ser registrados no React Native. Para Android, o pacote é adicionado no MainApplication.java no método getPackages(). Para iOS, o módulo é registrado automaticamente através da macro RCT_EXPORT_MODULE, mas o registro manual no AppDelegate.mm também pode ser usado.

java
// MainApplication.java — adicionando pacote ao React Native
import com.yourapp.nativemodules.ImageCompressorPackage;

public class MainApplication extends Application
    implements ReactApplication {

    private final ReactNativeHost mReactNativeHost =
        new ReactNativeHost(this) {

        @Override
        protected List<ReactPackage> getPackages() {
            List<ReactPackage> packages =
                new PackageList(this).getPackages();
            packages.add(new ImageCompressorPackage());
            return packages;
        }
    };
}

Após o registro, o módulo fica disponível em JavaScript através de NativeModules. O React Native usa automaticamente o nome do módulo especificado no getName() para Android ou RCT_EXPORT_MODULE para iOS.

js
// Usando Native Module do JavaScript
import { NativeModules } from 'react-native';
import { Platform } from 'react-native';

const ImageCompressor = NativeModules.ImageCompressor;

async function compressPhoto(uri: string) {
  try {
    const result = await ImageCompressor.compressImage(
      uri.replace('file://', ''), 0.8
    );
    console.log('Comprimido:', result);
    return result;
  } catch (error) {
    console.error('Falha na compressão:', error);
    throw error;
  }
}

Native Module vs Turbo Module: comparação

A comparação do Native Module clássico com o Turbo Module ajuda a entender qual abordagem escolher para um novo projeto. Ambos os mecanismos fornecem acesso ao código nativo, mas diferem fundamentalmente em arquitetura e desempenho.

CaracterísticaNative Module (Bridge)Turbo Module (JSI)
ComunicaçãoAssíncrona via JSONSíncrona via JSI
SerializaçãoJSON em cada chamadaSem cópia de dados
TipagemManual, sem geraçãoAutomática via Codegen
CarregamentoNa inicialização da aplicaçãoCarregamento lento (lazy load)
CompatibilidadeTodas as versões do React NativeReact Native 0.73+

Para projetos existentes no React Native 0.72 e anteriores, os Native Modules clássicos continuam sendo a escolha principal. Para novos projetos, o Turbo Module é recomendado, especialmente se alto desempenho for necessário em chamadas frequentes a métodos nativos. Com a atualização gradual do React Native, a comunidade está avançando para uma transição completa para a nova arquitetura.

Perguntas frequentes

É possível passar um callback em vez de Promise em um Native Module?

Sim, o Bridge suporta callbacks. Em vez de Promise, podem ser usadas funções de callback RCTResponseSenderBlock no iOS e Callback no Android. No entanto, Promise é considerado o padrão moderno e é recomendado para novos módulos.

Como depurar um Native Module no Xcode ou Android Studio?

Um Native Module é depurado como código nativo normal — defina pontos de interrupção no Xcode ou Android Studio. Para iOS, use o esquema de compilação com React Native; para Android, use a configuração Debug. O ponto de entrada são os métodos chamados do JS.

Quais tipos de dados o Native Module NÃO suporta?

Native Module através do Bridge não suporta dados binários (NSData/byte[]), objetos personalizados e funções. Para imagens, use um caminho de arquivo ou string base64. O Turbo Module via JSI remove algumas dessas limitações.

Preciso reescrever um Native Module ao atualizar o React Native?

Geralmente não — a API do Native Module é estável e compatível com versões anteriores. Ao fazer a transição para a nova arquitetura (Turbo Module), o módulo é adaptado através de decoradores, mas o código existente continua funcionando.

Como enviar um evento de um Native Module para JavaScript?

Use RCTEventEmitter no iOS ou DeviceEventEmitter no Android. O módulo envia um evento e o lado JS se inscreve através do NativeEventEmitter do react-native. Isso é útil para dados em streaming e eventos de sensores.

Resumo

  • Native Module — uma classe Java/Objective-C que torna as APIs nativas acessíveis a partir do JavaScript no React Native.
  • Registro — iOS usa RCT_EXPORT_MODULE, Android usa ReactContextBaseJavaModule com @ReactMethod.
  • Invocação — do JS o módulo é acessado via NativeModules.Module e suporta Promise e callback.
  • Tipos de dados — o Bridge apenas transfere tipos compatíveis com JSON: strings, números, arrays, dicionários.
  • Desempenho — o Native Module clássico é inferior ao Turbo Module devido à assincronicidade e serialização.
  • Migração — ao atualizar para React Native 0.76+ o módulo pode funcionar como Turbo Module sem reescrever a lógica.
  • Aplicação — Native Module é indispensável para câmera, Bluetooth, sistema de arquivos e integração de SDK.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também