Noções Básicas de Brownfield
10 exemplos para decidir quando incorporar React Native, reescrever em RN ou usar WebView como fallback - 7 básicos e 3 intermediários.
Busque em todas as páginas da documentação
10 exemplos para decidir quando incorporar React Native, reescrever em RN ou usar WebView como fallback - 7 básicos e 3 intermediários.
O trabalho em Brownfield assume que você já envia um aplicativo nativo iOS e/ou Android cujo ponto de entrada principal não é React Native. Você adiciona RN como uma biblioteca, tela ou módulo de recurso.
Para integração prática após essas decisões, veja Visão Geral do expo-brownfield. Para limites de arquitetura dentro da fatia RN, veja Noções Básicas de Arquitetura Mobile.
# Spike: projeto Expo mínimo para validar viabilidade de incorporação
npx create-expo-app@latest RnSpike --template blank-typescript@sdk-57
cd RnSpike
npx expo install expo-brownfieldFerramentas: Estes exemplos visam o Expo SDK 57 (
expo~57.0.4), React Native 0.86.0 e React 19.2.3. As APIs de integração Brownfield estão em alfa - reserve tempo para depuração de build nativo.
Greenfield significa que React Native (ou Expo) é a raiz do aplicativo - cada tela se ramifica a partir de uma entrada JS. Brownfield significa que UIKit/Swift, Jetpack Compose/Kotlin ou outra pilha nativa possui o shell; RN é incorporado sob demanda.
Greenfield Brownfield
┌─────────────────────┐ ┌─────────────────────┐
│ Raiz RN (principal) │ │ Raiz Nativa │
│ ├─ Aba A │ │ ├─ Home (nativo) │
│ ├─ Aba B │ │ ├─ Configurações (nat.) │
│ └─ Modal │ │ └─ Checkout (RN) │ ← Ilha RN
└─────────────────────┘ └─────────────────────┘Relacionado: Visão Geral do expo-brownfield - empacotando RN como AAR/XCFramework | ../architecture-design/mobile-architecture-basics/mobile-architecture-basics.md - estruturando a fatia RN uma vez incorporada
Incorpore RN quando o recurso precisar de UI com aparência nativa, comportamento offline/cache, APIs de dispositivo ou TypeScript compartilhado com uma equipe web - e a tela nativa legada levaria trimestres para ser reconstruída.
| Sinal | Incorporar RN |
|---|---|
| Formulários complexos com validação | ✓ |
| Listas com gestos / Reanimated | ✓ |
| Câmera, biometria, BLE via módulos Expo | ✓ |
| Reutilizar sistema de design React existente | ✓ |
| Uma página estática de FAQ | ✗ (WebView ou nativo) |
// A fatia RN ainda pode usar o Expo Router dentro do contêiner fornecido pelo host
// app/_layout.tsx - a navegação RN é interna ao módulo incorporado
import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="checkout" />
<Stack.Screen name="order-confirmation" />
</Stack>
);
}expo-brownfield quando as equipes nativas recusarem Node no CImultipleFrameworks no iOSRelacionado: RNHostView & Incorporação de UI Nativa - RN dentro de layouts SwiftUI/Compose | ../native-modules/native-modules-basics/native-modules-basics.md - quando apenas JS é insuficiente
Uma reescrita substitui o shell nativo por um aplicativo Expo/RN. Justificada quando a maioria das telas está mudando de qualquer maneira, os custos de navegação dupla excedem a migração, ou você precisa de um pipeline OTA para todo o produto.
| Sinal | Reescrever |
|---|---|
| >60% do roadmap toca UI compartilhada com a web | ✓ |
| Código-fonte nativo incontrolável (sem testes, sem proprietários) | ✓ |
| Conformidade da loja exige identidade de binário única | ✓ |
| Uma equipe possui um recurso em um aplicativo caso contrário estável | ✗ (incorporar) |
Lista de verificação de decisão de reescrita (todos "sim" → forte caso de reescrita):
□ Nativo e web mobile compartilham uma biblioteca de componentes
□ Autenticação, push e links profundos precisam de roteamento unificado (Expo Router)
□ A equipe nativa não consegue sustentar duas pilhas de UI
□ A liderança aceita 2–4 sprints de congelamento de migração nos fluxos afetadosRelacionado: ADR de Adoção Incremental - decisões de strangler fig classificadas | ../architecture-design/adr-navigation-library-choice/adr-navigation-library-choice.md - Expo Router vs React Navigation durante a migração
WebView envia a web mobile dentro do shell nativo. Melhor para conteúdo somente leitura, raramente atualizado ou hospedado legalmente, onde UX nativa e offline não são metas.
import { WebView } from "react-native-webview";
export function HelpCenterWebView({ url }: { url: string }) {
return (
<WebView
source={{ uri: url }}
startInLoadingState
sharedCookiesEnabled
// O aplicativo host deve injetar cookie de autenticação ou token via injectedJavaScript
/>
);
}| Sinal | WebView |
|---|---|
| Marketing / legal / central de ajuda | ✓ |
| Ferramentas internas de administração usadas mensalmente | ✓ |
| Checkout com restrições PCI | ✗ (nativo ou RN + SDK certificado) |
| Aplicativo de campo com foco offline | ✗ |
Relacionado: Autenticação Compartilhada e Bridges - passando sessões para WebView e RN
Pontue cada recurso candidato de 1 a 5 nesses eixos; o total mais alto sugere a coluna.
| Eixo | Incorporar RN | Reescrever | WebView |
|---|---|---|---|
| Complexidade da UI | Formulários/listas complexos | Aplicativo inteiro | HTML estático |
| Necessidade offline | Necessário | Necessário | Opcional |
| Cadência de lançamento | Recurso semanal | Versão principal | Mudança rara de cópia |
| Capacidade da equipe nativa | Baixa | Disposta a sair da UI nativa | Qualquer |
| Código compartilhado com a web | Alto | Alto | Já na web |
| Tolerância a risco | Média (ilha) | Alta (plataforma) | Baixa |
Exemplo: "Rastreamento de pedidos" em um aplicativo de varejo nativo
Complexidade da UI: 4 | Offline: 3 | Cadência: 5 | TS Compartilhado: 4
→ Incorporar RN (strangler fig na pilha de rastreamento)
Exemplo: "Termos de serviço"
UI: 1 | Offline: 0 | Cadência: 1
→ WebView ou SFSafariViewControllerRelacionado: ADR de Adoção Incremental - decisões classificadas formais por cenário
O Expo documenta duas formas de brownfield:
| Abordagem | Localização RN | CI Nativo precisa de Node? | Melhor para |
|---|---|---|---|
| Integrada | Projeto RN envolve ou fica ao lado de ios//android/ nativos | Sim | Equipe única, mudanças frequentes entre fronteiras |
| Isolada | Repositório separado/pacote monorepo → AAR + XCFramework | Não (consome artefatos) | Esquadrões nativos e RN separados |
// app.config.ts - caminho isolado usa o plugin de configuração expo-brownfield
export default {
expo: {
plugins: [
[
"expo-brownfield",
{
ios: { targetName: "CheckoutBrownfield" },
android: {
group: "com.example",
libraryName: "checkout-brownfield",
version: "2.1.0",
},
},
],
],
},
};# Esquadrão RN publica artefatos; esquadrão nativo consome Maven / Swift Package
npx expo-brownfield build:android --release
npx expo-brownfield build:ios --release --package CheckoutPackagenpx expo prebuild dentro do repositório hostRelacionado: Visão Geral do expo-brownfield - cookbook completo | ../native-modules/config-plugins/config-plugins.md - opções do plugin
expo-brownfield
O aplicativo host inicializa o runtime RN uma vez e, em seguida, apresenta um view controller ou activity.
// iOS - chamar cedo no AppDelegate
import CheckoutBrownfield
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
ReactNativeHostManager.shared.initialize()
return true
}
}
// UIKit - empurrar checkout do React Native
let vc = ReactNativeViewController(
moduleName: "main",
initialProps: ["cartId": cartId]
)
navigationController?.pushViewController(vc, animated: true)// Android - BrownfieldActivity + fragment
class CheckoutActivity : BrownfieldActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
showReactNativeFragment()
}
}moduleName deve corresponder ao registro main em registerRootComponent / app.json no projeto RNinitialProps semeia parâmetros de rota - combine com chaves de autenticação compartilhadas (próximo artigo)npx expo start); builds de lançamento usam o bundle incorporado dentro do artefatoRelacionado: CI/CD Brownfield - pipelines de artefatos de depuração vs. lançamento
Escolha um fluxo que seja doloroso em nativo, delimitado e não no caminho crítico de inicialização a frio.
Boas primeiras fatias:
✓ Dashboard pós-login (autenticação já nativa)
✓ Subfluxo de configurações raramente aberto no lançamento
✓ Novo recurso sem código legado
Más primeiras fatias:
✗ Splash de inicialização do aplicativo / aba inicial (custo de inicialização a frio + inicialização RN)
✗ Hub de navegação nativa profunda (propriedade da pilha de retorno incerta)
✗ Tarefas em segundo plano / widgets (RN não em processo)// Lado RN - escutar mensagens "abrir fatia" nativas
import * as Brownfield from "expo-brownfield";
import { useEffect } from "react";
import { router } from "expo-router";
export function useNativeDeepLinks() {
useEffect(() => {
const sub = Brownfield.addMessageListener((event) => {
if (event.type === "OPEN_TRACKING" && event.orderId) {
router.push(`/orders/${event.orderId}`);
}
});
return () => sub.remove();
}, []);
}Brownfield.popToNative() quando o RN terminar um fluxo e devolver o controle para UIKit/ComposeRelacionado: Autenticação Compartilhada e Bridges - contrato
BrownfieldMessaging
Quando o gerente de produto pede "apenas use o site mobile", execute esta comparação em uma tela.
| Critério | WebView | RN Incorporado |
|---|---|---|
| Desempenho de rolagem em Android de baixo custo | Travado | Virtualização de lista nativa |
| Puxar para atualizar | Bridge personalizado | RefreshControl |
| Link profundo push para linha | Fragilidade do URL | Caminho tipado do Expo Router |
| "Funcionalidade mínima" da App Store | Risco se shell fino | Presença nativa mais forte |
| Custo de engenharia neste trimestre | Dias | Semanas |
// Compromisso: WebView para spike MVP, RN para v2 - controlar com flag de recurso
import { useFeatureFlag } from "@/shared/feature-flags";
export function LoyaltyScreen() {
const useNative = useFeatureFlag("loyalty_rn_v2");
return useNative ? <LoyaltyNative /> : <LoyaltyWebView uri="https://m.example.com/loyalty" />;
}Relacionado: ../architecture-design/modular-monolith-vs-multi-app/modular-monolith-vs-multi-app.md - flags de recursos vs. aplicativos separados
Defina quem é o proprietário de cada camada antes que o primeiro PR seja enviado.
Esquadrão Nativo é proprietário de:
- Inicialização do aplicativo, registro push, cofre de keychain/sessão
- Apresentação de ReactNativeViewController / BrownfieldActivity
- Binários da loja, assinatura, triagem de falhas nativas
Esquadrão RN é proprietário de:
- Projeto Metro, pino do Expo SDK, qualidade do bundle JS
- Telas dentro do módulo incorporado, política OTA (se habilitada)
- Atualizações de versão do artefato expo-brownfield
Contrato compartilhado (documentar no repositório):
- Tipos de mensagem: OPEN_*, SESSION_*, LOGOUT
- Chaves de estado compartilhadas: auth.accessToken, auth.userId
- Versionamento semântico em AAR/Maven + iOS Swift Package// Versione o contrato da bridge - alterações que quebram exigem bump de artefato principal
export const BRIDGE_CONTRACT_VERSION = "1.2.0";
export type HostToRnMessage =
| { type: "SESSION_UPDATED"; accessToken: string; userId: string }
| { type: "OPEN_CHECKOUT"; cartId: string };multipleFrameworks: true no iOS correm risco de símbolos duplicadosRelacionado: Melhores Práticas de Brownfield - resumo de 25 itens | ../native-modules/autolinking-and-expo-modules-core/autolinking-and-expo-modules-core.md - autolinking em monorepos
Não - o Expo Go é um contêiner greenfield. Use artefatos expo-brownfield de depuração com Metro, ou um cliente de desenvolvimento integrado dentro do aplicativo host.
Sim, para o módulo RN quando as versões do runtime se alinham - mas o binário da loja host ainda precisa ser enviado quando as dependências nativas mudam. Veja CI/CD Brownfield.
RN quando você precisa de offline, integração de navegação nativa ou componentes compartilhados com a web. WebView quando o conteúdo é raramente atualizado e pertence a uma equipe web separada sem capacidade mobile.
Um é o mais simples. Múltiplas frameworks isoladas no iOS exigem multipleFrameworks: true e gerenciamento cuidadoso de símbolos - planeje em um ADR antes de duplicar projetos Expo.
expo-brownfieldVersões da Stack: Esta página foi escrita para React 19.2.3, React Native 0.86.0 e Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026