Expo Router no React Native: Guia Completo de Navegação Baseada em Arquivos
Dominar o Expo Router para React Native com este guia cobrindo roteamento baseado em arquivos, layouts, rotas dinâmicas, navegação tipada e padrões avançados como modais e tabs.

O Expo Router traz roteamento baseado em arquivos para o React Native, substituindo a configuração manual de navegação por uma abordagem baseada em convenções inspirada no Next.js. A partir do SDK Expo 57 e do Expo Router, construir navegação multiplataforma para Android, iOS e web requer apenas criar arquivos no diretório correto.
Novos projetos Expo já vêm com o Expo Router pré-configurado. Execute npx create-expo-app@latest --template default para começar com roteamento baseado em arquivos pronto para uso. Projetos existentes podem adicioná-lo instalando expo-router e atualizando o ponto de entrada.
Como funciona o roteamento baseado em arquivos no Expo Router
Cada arquivo dentro do diretório app se torna automaticamente uma rota. O caminho do arquivo mapeia diretamente para o caminho da URL, eliminando a necessidade de uma configuração de navegação centralizada. Um arquivo em app/settings.tsx cria uma rota /settings, enquanto app/profile/edit.tsx mapeia para /profile/edit.
Esta abordagem oferece três vantagens principais sobre a configuração tradicional do React Navigation:
- Zero configuração: as rotas existem no momento em que o arquivo é criado
- Deep linking automático: cada tela obtém uma URL, habilitando compartilhamento e testes
- Navegação tipada: TypeScript sabe quais rotas existem em tempo de compilação
import { View, Text, StyleSheet } from 'react-native'
import { Link } from 'expo-router'
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome</Text>
{/* Link maps directly to file path */}
<Link href="/settings" style={styles.link}>
Open Settings
</Link>
<Link href="/profile/edit" style={styles.link}>
Edit Profile
</Link>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 24 },
title: { fontSize: 28, fontWeight: 'bold', marginBottom: 16 },
link: { fontSize: 16, color: '#61DAFB', marginTop: 12 },
})O componente Link lida com a navegação em todas as plataformas. Na web, renderiza uma tag âncora com atributos href apropriados para SEO. Em plataformas nativas, dispara navegação baseada em stack.
Estrutura do projeto e arquivos de layout
O Expo Router usa arquivos _layout.tsx para definir contêineres de navegação. Cada diretório pode ter seu próprio layout, criando hierarquias de navegação aninhadas. O layout raiz envolve toda a aplicação, enquanto layouts aninhados controlam seções específicas.
Uma estrutura de projeto típica se apresenta assim:
app/
_layout.tsx # Root layout (Stack or custom)
index.tsx # Home screen (/)
(tabs)/ # Tab group (parentheses = route group)
_layout.tsx # Tab navigator
home.tsx # /home tab
search.tsx # /search tab
profile.tsx # /profile tab
settings/
_layout.tsx # Settings stack layout
index.tsx # /settings
notifications.tsx # /settings/notifications
privacy.tsx # /settings/privacyGrupos de rotas, diretórios envolvidos em parênteses, organizam arquivos sem afetar a URL. O diretório (tabs) acima cria um navegador de abas, mas as URLs permanecem /home, /search e /profile em vez de /tabs/home.
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: '#1a1a2e' },
headerTintColor: '#ffffff',
headerTitleStyle: { fontWeight: '600' },
}}
>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ title: 'Settings' }} />
</Stack>
)
}O layout raiz também serve como o local para carregar fontes, inicializar providers e configurar ajustes globais.
Construindo navegação por abas com Expo Router
A navegação por abas requer um arquivo _layout.tsx dentro de um grupo de rotas. O Expo Router fornece tanto NativeTabs para experiências de abas específicas por plataforma quanto o componente padrão Tabs para a maioria dos casos de uso. Desde o SDK 56, Native Tabs suporta a propriedade disabled para prevenir seleção de abas enquanto as mantém visíveis.
import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons'
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#61DAFB',
tabBarInactiveTintColor: '#888',
tabBarStyle: {
backgroundColor: '#1a1a2e',
borderTopColor: '#2d2d44',
},
}}
>
<Tabs.Screen
name="home"
options={{
title: 'Home',
tabBarIcon: ({ color, size }) => (
<Ionicons name="home" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="search"
options={{
title: 'Search',
tabBarIcon: ({ color, size }) => (
<Ionicons name="search" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profile',
tabBarIcon: ({ color, size }) => (
<Ionicons name="person" size={size} color={color} />
),
}}
/>
</Tabs>
)
}Cada arquivo de tela de aba exporta um componente React padrão. O ícone da barra de abas, rótulo e badge são configurados através da prop options no layout.
Rotas dinâmicas e parâmetros de rota
Segmentos dinâmicos usam colchetes no nome do arquivo. Um arquivo chamado [id].tsx corresponde a qualquer segmento único, enquanto [...slug].tsx captura todos os segmentos restantes.
import { View, Text, StyleSheet } from 'react-native'
import { useLocalSearchParams, Stack } from 'expo-router'
export default function ProductScreen() {
// Extract the dynamic parameter from the URL
const { id } = useLocalSearchParams<{ id: string }>()
return (
<View style={styles.container}>
<Stack.Screen options={{ title: `Product ${id}` }} />
<Text style={styles.heading}>Product Details</Text>
<Text style={styles.id}>ID: {id}</Text>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
heading: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 },
id: { fontSize: 16, color: '#888' },
})Navegar para /product/42 renderiza esta tela com id definido como "42". O hook useLocalSearchParams fornece acesso tipado a todos os parâmetros de rota.
Para rotas catch-all, [...slug].tsx captura segmentos de caminho inteiros:
import { useLocalSearchParams } from 'expo-router'
export default function DocsScreen() {
// /docs/getting-started/installation → slug = ['getting-started', 'installation']
const { slug } = useLocalSearchParams<{ slug: string[] }>()
return <DocViewer path={slug.join('/')} />
}Pronto para mandar bem nas entrevistas de React Native?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Rotas tipadas para segurança em tempo de compilação
O Expo Router gera tipos de rotas automaticamente quando typed routes está habilitado. Isso detecta links quebrados em tempo de compilação em vez de em tempo de execução.
Habilitar rotas tipadas no app.json:
{
"expo": {
"experiments": {
"typedRoutes": true
}
}
}Uma vez habilitado, a prop href no Link e o argumento para router.push() só aceitam strings de rotas válidas:
import { router } from 'expo-router'
function handleCheckout(cartId: string) {
// TypeScript validates this route exists
router.push(`/product/${cartId}`)
// This would cause a compile error if /nonexistent doesn't exist
// router.push('/nonexistent')
}Rotas tipadas funcionam bem com useLocalSearchParams. Os tipos gerados garantem que os nomes dos parâmetros correspondam entre a definição da rota e o componente consumidor, prevenindo bugs sutis que só aparecem ao navegar para uma tela específica.
Telas modais e opções de apresentação
Modais no Expo Router são telas regulares configuradas com presentation: 'modal' no layout. Esta abordagem mantém a convenção baseada em arquivos intacta: um modal é apenas outra rota.
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
{/* Modal screen slides up from the bottom */}
<Stack.Screen
name="create-post"
options={{
presentation: 'modal',
headerTitle: 'New Post',
}}
/>
</Stack>
)
}import { View, TextInput, Button, StyleSheet } from 'react-native'
import { router } from 'expo-router'
import { useState } from 'react'
export default function CreatePostModal() {
const [title, setTitle] = useState('')
const handleSubmit = () => {
// Submit logic here
router.back() // Dismiss the modal
}
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Post title"
value={title}
onChangeText={setTitle}
/>
<Button title="Publish" onPress={handleSubmit} />
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
input: {
borderWidth: 1,
borderColor: '#333',
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 16,
},
})Navegar para /create-post dispara a apresentação modal. A chamada router.back() a fecha, retornando à tela anterior no stack.
Navegação programática e a API do Router
Além do componente Link, o Expo Router fornece uma API imperativa através do objeto router. Isso lida com navegação disparada por lógica de negócio em vez de interações do usuário.
import { router } from 'expo-router'
// Push a new screen onto the stack
router.push('/profile/settings')
// Replace the current screen (no back button)
router.replace('/login')
// Go back to the previous screen
router.back()
// Navigate with parameters
router.push({
pathname: '/product/[id]',
params: { id: '42', source: 'recommendations' },
})
// Check if going back is possible
import { useRouter } from 'expo-router'
function BackButton() {
const router = useRouter()
return router.canGoBack() ? (
<Button title="Back" onPress={() => router.back()} />
) : null
}A distinção entre router.push e router.replace importa para fluxos de autenticação no React Native. Após um login bem-sucedido, router.replace('/dashboard') previne que o usuário navegue de volta para a tela de login.
router.replace() substitui a entrada atual no histórico de navegação. Para guardas de autenticação que redirecionam usuários não autenticados, usar <Redirect href="/login" /> dentro do render do componente: dispara durante a fase de render e funciona corretamente com server-side rendering na web.
Middleware e proteção de rotas
O Expo Router fornece middleware de servidor para lógica em nível de rota. O arquivo +middleware.ts intercepta requisições antes que cheguem ao componente da rota, habilitando verificações de autenticação, redirecionamentos e manipulação de headers.
import { type MiddlewareRequest } from 'expo-router/server'
export function middleware(request: MiddlewareRequest) {
const { pathname } = request.nextUrl
// Protect dashboard routes
const protectedPaths = ['/dashboard', '/settings', '/profile']
const isProtected = protectedPaths.some(p => pathname.startsWith(p))
if (isProtected) {
const token = request.cookies.get('session')
if (!token) {
return Response.redirect(new URL('/login', request.url))
}
}
return undefined // Continue to route
}Em plataformas nativas onde o middleware de servidor não executa, a proteção de rotas depende de guardas do lado do cliente. Um padrão comum envolve layouts protegidos com uma verificação de autenticação:
import { Redirect, Stack } from 'expo-router'
import { useAuth } from '@/hooks/useAuth'
export default function AuthenticatedLayout() {
const { isLoggedIn, isLoading } = useAuth()
if (isLoading) return null
if (!isLoggedIn) return <Redirect href="/login" />
return <Stack />
}O middleware de servidor executa apenas na web com server-side rendering. Para aplicativos nativos, sempre implementar guardas de rota do lado do cliente em componentes de layout. Combinar ambas as abordagens garante proteção consistente em todas as plataformas.
Migração SDK 56: Fork do React Navigation
A partir do SDK Expo 56, o Expo Router não depende mais dos pacotes @react-navigation/*. Se o projeto importa diretamente do React Navigation, atualizar os imports:
| Pacote Original | Nova Localização |
|---|---|
@react-navigation/native | expo-router/react-navigation |
@react-navigation/stack | expo-router/js-stack |
@react-navigation/bottom-tabs | expo-router/js-tabs |
Executar o codemod para atualizar imports automaticamente:
npx expo-codemod sdk-56-expo-router-react-navigation-replace srcA API em tempo de execução permanece inalterada. Apenas os especificadores de módulo foram movidos. Bibliotecas de terceiros que ainda importam de @react-navigation/core funcionam automaticamente porque o Expo CLI reescreve esses imports durante o bundling.
Fontes
- Documentação do Expo Router - guia oficial e referência de API
- Changelog SDK Expo 57 - notas da versão estável atual
- Post de blog Expo Router v56 - explicação do fork do React Navigation
- Guia de migração SDK 55 para 56 - passos de migração de imports
O que os entrevistadores esperam sobre Expo Router
- O Expo Router mapeia automaticamente cada arquivo no diretório
apppara uma rota, eliminando configuração manual de navegação - Layouts definidos através de arquivos
_layout.tsxcriam hierarquias de navegação para stacks, tabs e drawers - Rotas dinâmicas com
[param].tsxe rotas catch-all com[...slug].tsxlidam com navegação parametrizada com suporte completo de TypeScript - Rotas tipadas detectam links de navegação quebrados em tempo de compilação quando habilitadas no
app.json - Telas modais, navegação programática via
router.push/replace/back, e middleware de servidor completam o toolkit de roteamento - Grupos de rotas usando diretórios com parênteses organizam código sem afetar URLs
- Guardas do lado do cliente em layouts lidam com autenticação no nativo, enquanto middleware de servidor cobre web
- Desde o SDK 56, imports de
@react-navigation/*devem ser atualizados para equivalentes deexpo-router/*
Para mais sobre arquitetura React Native e preparação para entrevistas, consultar o módulo de módulos nativos.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Você saberia encontrar o bug em React Native?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 6 de outubro de 2026
Tags
Compartilhar
Artigos relacionados

Desenvolvimento de Aplicativos React Native em 2026: Guia Completo e Perguntas de Entrevista
Domine o desenvolvimento de aplicativos React Native em 2026 com a Nova Arquitetura, Hermes V1 e Fabric. Inclui exemplos de código práticos e perguntas de entrevista técnicas.

React Native 0.87 e SwiftPM em 2026: Build iOS Moderno e Perguntas de Entrevista
React Native 0.87 introduz suporte experimental ao Swift Package Manager, eliminando CocoaPods dos builds iOS. Este guia cobre a configuração do SwiftPM, a API TypeScript Estrita, otimizações do Metro 0.87, e perguntas de entrevista para desenvolvedores mobile seniores.

Hermes V1 no React Native 0.84: Performance, Bytecode Pré-compilado e Perguntas de Entrevista
Análise aprofundada das otimizações de performance do Hermes V1 no React Native 0.84: pré-compilação de bytecode, garbage collector Hades, gerenciamento de memória e perguntas técnicas essenciais para desenvolvedores mobile.