Boas Práticas Avançadas do Expo Router
Um resumo condensado das 25 melhores práticas mais importantes extraídas de todas as páginas desta seção.
Busque em todas as páginas da documentação
Um resumo condensado das 25 melhores práticas mais importantes extraídas de todas as páginas desta seção.
Aninhe um navegador por _layout.tsx: Cada layout de pasta exporta exatamente um Stack, Tabs, NativeTabs, SplitView ou Slot/Navigator sem cabeça - misturar dois navegadores em um arquivo de layout cria um histórico ambíguo.
Dê a cada aba sua própria pilha ao empurrar detalhes: Coloque home/_layout.tsx → <Stack /> sob cada aba que precisa de lista → detalhe; pastas de aba planas perdem pilhas de volta independentes.
Navegue com href absolutos entre limites de abas: Prefira router.push('/inbox/t2') em vez de caminhos relativos ../ ao cruzar abas - segmentos relativos são resolvidos contra o contexto do navegador atual.
Ancore rotas modais para deep links: Exporte unstable_settings = { anchor: '(tabs)' } na pilha que possui modais para que /compose de cold-start não limpe o shell da aba.
Use router.replace após transições de autenticação: Login, logout e mudanças de função devem substituir o histórico - push permite que os usuários voltem deslizando para telas de credenciais ou com função desatualizada.
Hidrate a sessão antes de renderizar Stack.Protected: Mantenha a splash screen visível até que a leitura do SecureStore / token seja concluída - renderizar guards enquanto isLoading causa cintilação e loops de redirecionamento.
Autoridade única para gates de autenticação: Stack.Protected raiz agrupa melhor do que espalhar <Redirect /> em cada layout aninhado - redirecionamentos concorrentes causam saltos infinitos entre / e /sign-in.
Nunca duplique telas entre grupos Protected: Um arquivo de rota pode aparecer em apenas uma declaração Stack.Protected - aninhe guards para funções em vez de copiar profile para dois grupos.
Trate Protected como UX, não segurança: Guards do lado do cliente não removem rotas do bundle - valide cada chamada de API no lado do servidor; veja ../auth-session/.
Escolha modais do Expo Router em vez de <Modal> do RN para fluxos linkáveis: Rotas com presentation: 'modal' participam de deep links e do botão voltar do Android - reserve o Modal do RN para alertas efêmeros sem URL.
Renderize o chrome da folha de formulário no conteúdo no Android: Cabeçalhos de pilha nativos e pilhas aninhadas dentro de formSheet não renderizam no Android - título e ações pertencem ao corpo da folha.
Limite os detents da folha de formulário a três no Android: sheetAllowedDetents com mais de três valores é truncado - projete folhas multiplataforma com ≤3 pontos de encaixe.
Declare todas as abas nativas com NativeTabs.Trigger: Ao contrário de Tabs em JavaScript, os arquivos não são adicionados automaticamente à barra - gatilhos ausentes significam que as rotas existem, mas são inacessíveis da UI da aba.
Não use disabled em abas nativas para autorização: disabled bloqueia apenas os toques na barra de abas - router.push ainda navega; use Stack.Protected ou hidden para remoção real.
Resolva a visibilidade da aba por feature-flag antes da primeira montagem: Alternar hidden em NativeTabs.Trigger em tempo de execução remonta o navegador e limpa as pilhas - decida os conjuntos de abas na inicialização.
Conduza o master-detail do tablet a partir de segmentos de URL: O estado de seleção pertence às rotas [id].tsx, não a useState(selectedId) - deep links e rotação restauram corretamente.
Forneça fallback responsivo de split para tablets Android: SplitView é alpha nativo do iOS - use useWindowDimensions + layout de linha e Stack por seção no Android e larguras estreitas.
Mostre um placeholder de detalhe vazio no tablet: /orders sem um id deve renderizar "Selecione um pedido" no painel secundário - a paridade do cliente de e-mail evita colunas em branco confusas.
Limite-se a um SplitView por aplicativo: Aninhar visualizações divididas ou colocá-las dentro de abas falha - split pertence à raiz com barras laterais substituindo abas inferiores no iPad.
Comece RSC apenas com Server Functions no SDK 57: Habilite reactServerFunctions dentro de rotas de cliente normais - evite reactServerComponentRoutes em produção até que o suporte a Stack/Tabs retorne.
Envolva UI de Server Function transmitida em Suspense: Cada chamada renderX() de "use server" precisa de <React.Suspense fallback={…}> - caso contrário, os estados de carregamento bloqueiam a tela inteira.
Alcance <Slot /> antes de Navigator personalizado: Chrome sem cabeça (cabeçalhos, progresso de onboarding) raramente precisa de useNavigationBuilder - navegadores personalizados aumentam a manutenção e a superfície de importação do SDK.
Importe primitivas de navegação de caminhos expo-router: SDK 56+ roteia @react-navigation/* através de expo-router e expo-router/react-navigation - importações diretas de pacotes quebram na atualização.
Passe o parâmetro de consulta redirect através do login: Deep links para conteúdo protegido devem continuar após o login via router.replace(redirect ?? '/(tabs)') - não descarte o destino pretendido.
Teste deep links de cold-start em matrizes de CI: Verifique myapp://inbox/t2, âncoras de modal, redirecionamentos de usuário deslogado e o botão voltar de hardware do Android em cada lançamento - regressões de navegação são incidentes de produção.
useSegments()).Stack.Protected / Tabs.Protected na raiz.app/index.tsx apenas para o caminho de pouso padrão.Tabs em JavaScript quando precisar de Tabs.Protected dentro do layout da aba ou JSX de barra de abas totalmente personalizado.isLoading antes de qualquer navegador.Stack.Protected raiz.router.replace após o login.index.tsx duplicado em grupos concorrentes.renderRouter para navegação por href e redirecionamentos Protected.Versões de 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: 19 de jul. de 2026