Durante anos, a bridge foi o elefante na sala do React Native. Toda comunicação entre JavaScript e código nativo passava por uma fila assíncrona que serializava dados em JSON, ida e volta, sem parar. Funcionava, mas cobrava seu preço em frames perdidos, latency spikes e uma experiência que nunca chegava perto de nativa.

A partir da versão 0.76, lançada em outubro de 2024, o React Native habilitou a New Architecture por padrão. Na 0.82, de outubro de 2025, tornou-a obrigatória. E na 0.84, a versão estável mais recente (fevereiro de 2026), o código legado da bridge está sendo progressivamente removido do repositório. Não existe mais caminho de volta.

Este post explica os três pilares dessa mudança (JSI, Fabric e TurboModules), mostra o que muda na prática com benchmarks reais, e aponta o caminho de migração para quem ainda está na arquitetura antiga.

O que a bridge fazia e por que virou gargalo

O React Native original rodava dois mundos separados: um thread JavaScript (com o motor Hermes ou JSC) e o lado nativo (UIKit no iOS, Android Views no Android). A ponte entre eles era a bridge, uma fila assíncrona de mensagens JSON.

O fluxo era assim:

  1. O JS decidia renderizar um componente e enviava uma mensagem JSON pela bridge
  2. O lado nativo recebia, desserializava, criava a view e calculava o layout
  3. Qualquer resposta (evento de toque, resultado de módulo nativo) voltava pela mesma fila

O problema é que tudo passava por serialização. Um array de 10.000 itens virava uma string JSON gigante, atravessava a bridge, e era parseado do outro lado. Em listas longas ou animações com muitas propriedades mudando por frame, a fila congestionava. O resultado: jank visível e aquele delay perceptível entre tocar um botão e ver a resposta.

Gestos eram particularmente afetados. Um pan gesture que precisava atualizar a posição de uma view a cada frame dependia de roundtrips JS-nativo. Se a bridge estivesse ocupada processando outras mensagens, o gesto travava.

JSI: a fundação que substituiu a bridge

O JSI (JavaScript Interface) é a base de tudo. Em vez de mensagens JSON assíncronas, o JSI expõe objetos C++ diretamente para o JavaScript como HostObjects. O JS segura uma referência de memória ao objeto nativo e chama métodos sobre ele de forma síncrona, sem serialização.

Na prática, isso significa que uma chamada nativa que antes precisava de:

// Arquitetura antiga: mensagem assíncrona via bridge
NativeModules.Storage.getString('user_token')
  .then(token => processToken(token));

Agora acontece via referência direta:

// Com JSI: acesso síncrono via HostObject
const token = global.__storageModule.getString('user_token');
processToken(token);

A diferença de throughput é brutal. Aplicações que processam buffers de câmera (frames de ~30 MB em tempo real) conseguem mover dados a ~2 GB/s pela interface JSI, algo impossível com serialização JSON.

O JSI também é a fundação para Fabric e TurboModules. Ambos dependem dessa interface C++ compartilhada para funcionar.

Fabric: o novo renderer

Fabric é o nome do novo sistema de renderização. Ele substitui o antigo UIManager por uma árvore de sombra (shadow tree) imutável escrita em C++ e compartilhada entre plataformas.

As mudanças principais:

  • Shadow tree imutável em C++: cada atualização de estado gera uma nova árvore em vez de mutar a existente. Isso permite diffing eficiente e, mais importante, torna o renderer thread-safe
  • Layout síncrono: useLayoutEffect funciona de verdade no React Native. Antes, o layout era calculado assincronamente no lado nativo, então ler dimensões no momento certo era um palpite
  • View flattening automático: containers que existem apenas para organização (sem estilo visual) são removidos da hierarquia nativa, reduzindo a profundidade da árvore de views
  • Rendering concorrente: com React 18/19 integrado, Fabric suporta useTransition, Suspense, useDeferredValue e batching automático de eventos nativos rápidos (como Slider)

O rendering concorrente merece atenção especial. Em uma lista com milhares de itens, o React pode interromper uma renderização em andamento se o usuário fizer scroll. Renderizações obsoletas são descartadas em vez de completadas, o que reduz trabalho desperdiçado.

import { useTransition, useState } from 'react';
import { View, TextInput, FlatList, Text } from 'react-native';

function SearchableList({ items }: { items: string[] }) {
  const [query, setQuery] = useState('');
  const [filtered, setFiltered] = useState(items);
  const [isPending, startTransition] = useTransition();

  function handleSearch(text: string) {
    setQuery(text);
    startTransition(() => {
      setFiltered(items.filter(item =>
        item.toLowerCase().includes(text.toLowerCase())
      ));
    });
  }

  return (
    <View>
      <TextInput value={query} onChangeText={handleSearch} />
      <FlatList
        data={filtered}
        renderItem={({ item }) => <Text>{item}</Text>}
        style={{ opacity: isPending ? 0.6 : 1 }}
      />
    </View>
  );
}

Nesse exemplo, o startTransition marca a filtragem como não-urgente. Se o usuário digitar outro caractere antes da filtragem terminar, o React descarta o trabalho anterior e começa de novo com o novo texto. O input responde instantaneamente.

TurboModules: módulos nativos sob demanda

Na arquitetura antiga, todos os módulos nativos eram inicializados no startup do app, mesmo os que nunca seriam usados naquela sessão. Um app com 30 módulos nativos registrados carregava todos os 30 antes de mostrar a primeira tela.

TurboModules resolve isso com inicialização lazy. Cada módulo só é carregado quando o JavaScript o requisita pela primeira vez. O impacto no tempo de startup é direto: menos módulos inicializados = menos trabalho antes da primeira tela.

A comunicação também muda. TurboModules usam JSI em vez da bridge, o que significa chamadas diretas sem serialização JSON. E o sistema de tipos é enforced em build time pelo CodeGen, que gera bindings tipados a partir de specs TypeScript ou Flow.

// turbo-module-spec.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  getDeviceId(): string;
  getBatteryLevel(): Promise<number>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('DeviceInfo');

O getEnforcing tenta carregar o módulo e lança um erro se ele não existir. Para módulos opcionais, existe TurboModuleRegistry.get() que retorna null. O CodeGen lê essa spec e gera o boilerplate nativo (Objective-C++ ou Java/Kotlin) automaticamente.

Um ponto que facilita a migração: a Interop Layer permite que módulos nativos antigos (escritos com a API da bridge) funcionem com a New Architecture sem reescrita. O módulo roda por cima do JSI de forma transparente. Não é tão eficiente quanto um TurboModule nativo, mas funciona.

Benchmarks: o que mudou na prática

Números de performance são sempre contextuais, então vou separar o que é documentação oficial do que vem de cases reais.

A Shopify publicou um estudo detalhado da migração do app Shop para a New Architecture. Os resultados mostram uma melhoria de ~10% no tempo de launch no Android. Entretanto, a mesma migração revelou que algumas telas complexas tiveram aumento de ~20% no tempo de carregamento, atribuído à forma como o Fabric recalcula layout em certas hierarquias de views.

A documentação oficial do React Native destaca os seguintes ganhos arquiteturais sem quantificar percentuais específicos:

  • Eliminação da serialização JSON remove um gargalo fixo por operação
  • O layout síncrono elimina uma classe inteira de bugs de posicionamento
  • A inicialização lazy de TurboModules reduz o trabalho de startup proporcionalmente ao número de módulos não utilizados
  • O rendering concorrente permite interromper renderizações obsoletas, reduzindo trabalho desperdiçado

A versão 0.84 traz duas novidades relevantes para o dia a dia. O Hermes V1, novo motor JavaScript padrão, melhora a performance de execução. Separadamente, binários iOS pré-compilados passam a vir habilitados por padrão, reduzindo o tempo de clean build em até 10x segundo as release notes da versão 0.81. O ganho real varia conforme o tamanho do projeto, mas elimina a recompilação do React Native a cada clean build.

O ponto honesto é que a New Architecture não é uma bala de prata para performance. Os ganhos são reais em operações que antes dependiam da bridge (comunicação JS-nativo frequente, gestos complexos, módulos nativos pesados). Mas telas puramente JS que não interagiam muito com o lado nativo podem não sentir diferença significativa.

Quem já migrou em produção

A Meta usa a New Architecture em seus apps (Facebook, Instagram) desde antes de ser o padrão. Eles desenvolveram a arquitetura internamente e a testaram em escala com bilhões de usuários.

A Shopify migrou o app Shop e documentou o processo publicamente no blog de engenharia. O relatório inclui tanto os ganhos quanto os problemas encontrados, o que é raro e valioso para quem está planejando a própria migração.

A Microsoft usa React Native no Office e em features do Copilot, atendendo mais de 600 milhões de usuários. Discord, Amazon e Coinbase também operam com React Native em produção, embora os detalhes específicos sobre adoção da New Architecture variem.

Para bibliotecas do ecossistema, a maioria dos pacotes populares já suporta a New Architecture. O FlashList da Shopify, por exemplo, foi reconstruído sobre Fabric. Reanimated, React Navigation e React Native Screens também oferecem suporte completo.

Como migrar da arquitetura antiga

Se o seu app ainda roda em versão anterior à 0.76, o caminho recomendado pela documentação oficial envolve etapas progressivas:

  1. Atualize o React Native para a versão mais recente. Cada minor version desde a 0.76 melhorou a estabilidade e as ferramentas de migração
  2. Audite suas dependências nativas. Use npx rn-chk-new-arch para identificar quais bibliotecas já suportam a New Architecture
  3. Confie na Interop Layer para módulos que ainda não migraram. Ela permite que módulos antigos funcionem sobre JSI sem reescrita
  4. Converta seus próprios módulos nativos para TurboModules gradualmente. Comece pelos mais simples e deixe os complexos para depois
  5. Teste gestos e animações com atenção especial. A mudança do UIManager para Fabric pode afetar componentes que dependiam do comportamento assíncrono antigo

Pontos de atenção na versão 0.84:

  • iOS mínimo: 15.1
  • Android SDK mínimo: 24
  • Node.js mínimo: 22.11
  • Gradle 9.0 obrigatório para Android
  • A CLI foi desacoplada em um pacote separado

O conselho mais prático que encontrei nos relatos de migração: não tente migrar e reescrever ao mesmo tempo. Atualize primeiro, valide que tudo funciona com a Interop Layer, e só depois converta módulos para TurboModules nativos onde fizer sentido.

Conclusão

A bridge morreu. Literalmente, o código foi removido na 0.84. Quem mantém apps em React Native não tem mais a opção de adiar a migração. A boa notícia é que a Interop Layer torna o processo gradual, e a maioria das bibliotecas populares já está pronta.

O que me chama atenção nessa transição é a honestidade dos benchmarks. A Shopify publicou que algumas telas ficaram mais lentas. A documentação oficial não promete ganhos mágicos de FPS. A New Architecture resolve problemas reais e específicos (a bridge era de fato um gargalo para comunicação frequente JS-nativo), mas não transforma um app mal otimizado em um app rápido. A arquitetura é melhor. O código ainda precisa ser bom.

Para quem está escolhendo um framework mobile em 2026, o React Native saiu dessa transição mais maduro. Rendering concorrente, tipos enforced em build time, módulos lazy e uma interface C++ compartilhada entre plataformas. Isso é infraestrutura séria.

Referências pesquisadas nesta publicação