React Navigation ou Expo Router: qual escolher para o react native

Um app, em boa parte das vezes, não é composto só por uma tela, mas sim, diferentes telas que são compostas por diversos componentes. Por isso a importância da navegação, para que se tenha uma conexão entre essas telas.
No ecossistema do React Native, duas das opções mais populares para navegação são o React Navigation e o Expo Router.
Neste artigo, vamos explorar as diferenças dessas duas tecnologias e ajudar você a fazer a melhor escolha para o seu projeto.
O que é o React Navigation?
Se você já aprendeu React.js para web, provavelmente já deve ter visto o React Router.
O React Navigation tem uma proposta parecida, mas adaptada para o ambiente mobile, com as mesmas facilidades de navegação, porém com recursos extras, como o controle de gestos e animações específicas para apps móveis.
O React Navigation é uma das bibliotecas mais populares para navegação em React Native e pode ser utilizado tanto em projetos React Native puros quanto em projetos baseados no Expo.
Ah, você prestou atenção nesse último ponto? Sim, o React Navigation é compatível com projetos Expo também, e isso não é só uma coincidência.
Na verdade, isso se deve principalmente ao fato de o Expo Router (que vamos explorar daqui a pouco) utilizar como base para sua construção o próprio React Navigation.
É isso aí dev, nada acontece por acaso. Mas agora, que tal analisarmos o React Navigation na prática?
Implementação com React Navigation
Vamos criar um aplicativo que terá duas telas: uma tela inicial que exibe uma lista de pessoas usuárias e uma tela de detalhes que apresenta informações sobre a pessoa selecionada.
Nós iremos utilizar o Expo para inicializar o projeto execute o comando:
npx create-expo-app@latest MeuApp
Após criar o projeto, entre na pasta do projeto e digite o comando:
npm run reset-project
Caso apareça essa pergunta: "Do you want to move existing files to /app-example instead of deleting them? (Y/n)", responda com "n", isso fará com que todo o template inicial seja descartado.
Também será necessário instalar as dependências necessárias para a navegação. Para isso, execute:
npm install @react-navigation/native @react-navigation/native-stack
Em seguida, instale também o resto das dependências necessárias com expo
:
npx expo install react-native-screens react-native-safe-area-context
Recomendo que você renomeie os arquivos index e layout para o formato .jsx
, porque nesse projeto não iremos utilizar Typescript, também iremos fazer a criação de uma pasta de screens que será responsável por armazenar as telas do nosso app
.
Nosso app
terá a seguinte estrutura:
MeuApp/
├── app/
│ ├── _layout.jsx
│ ├── index.jsx
│ └── screens/
│ ├── details_screen.jsx
│ ├── home_screen.jsx
Para configurar a navegação no React Navigation, dentro do arquivo _layout.jsx
, é necessário importar os componentes e definir a estrutura de navegação utilizando um Stack.Navigator
.
Como no nosso app teremos apenas uma tela de Home
e Details
, o nosso _layout.jsx
irá ficar da seguinte forma:
import * as React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from './screens/home_screen';
import DetailsScreen from './screens/details_screen';
const Stack = createNativeStackNavigator();
export default function RootLayout() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
Esse código define a estrutura de navegação do aplicativo, permitindo transitar entre as telas Home
e Details
.
Nosso index.jsx ficará da seguinte maneira:
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import RootLayout from './_layout';
export default function Index() {
return (
<NavigationContainer>
<RootLayout />
</NavigationContainer>
);
}
Agora, podemos criar de fato a tela de home e de details. a HomeScreen
irá conter um botão que redireciona a pessoa usuária para a DetailsScreen
, passando o userId
e o email
ao navegar para DetailsScreen
:
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
export default function HomeScreen({ navigation }) {
const users = [
{ id: 1, name: 'Alice', email: '[email protected]' },
{ id: 2, name: 'Bob', email: '[email protected]' },
{ id: 3, name: 'Charlie', email: '[email protected]' },
{ id: 4, name: 'David', email: '[email protected]' },
];
return (
<View style={styles.container}>
{users.map((user) => (
<TouchableOpacity
key={user.id}
style={styles.button}
onPress={() => navigation.navigate('Details', { userId: user.id, email: user.email })}
>
<Text style={styles.buttonText}>Ver detalhes de {user.name}</Text>
</TouchableOpacity>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F8F9FA',
padding: 20,
},
button: {
backgroundColor: '#007BFF',
width: '80%',
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 3,
elevation: 3,
},
buttonText: {
color: '#FFF',
fontSize: 16,
fontWeight: 'bold',
},
});
Neste código, com o array users
e utilizando o método .map
estamos renderizando de forma dinâmica uma lista de pessoas usuárias
Na DetailsScreen
, é possível acessar o userId
e o email
através de route.params
e exibir a informação na interface.
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function DetailsScreen({ route }) {
const { userId, email } = route.params;
return (
<View style={styles.container}>
<Text style={styles.title}>Detalhes da pessoa</Text>
<Text style={styles.info}>ID: {userId}</Text>
<Text style={styles.info}>Email: {email}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFF',
padding: 20,
},
title: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 12,
color: '#333',
},
info: {
fontSize: 18,
marginBottom: 8,
color: '#555',
},
});
Com o app já criado, para inicializá-lo, utilize o :
npx expo start
Lembre-se que caso queira abrir em um dispositivo android deve utilizar um dispositivo físico ou ter um emulador instalado e executando.
Pronto, conseguimos inicializar um projeto com React Native e utilizar o React Navigation nele.
Agora, chegou o momento de conhecermos o Expo Router!

O que é o Expo Router?
O Expo Router é uma das maneiras mais utilizadas para implementar a navegação em apps desenvolvidos no ambiente Expo.
Ele se baseia no conceito de navegação por arquivos, o que torna a estrutura do código mais intuitiva.
Sua principal vantagem está na configuração automática dentro do Expo, reduzindo a necessidade de ajustes manuais e simplificando a implementação.
Além disso, sua abordagem baseada em URLs facilita a integração com deep linking e torna a passagem de parâmetros mais direta.
Para quem está começando no desenvolvimento de aplicativos, essa pode ser uma excelente opção por ser mais simples e rápida de configurar.
Agora usando o Expo Router, vamos implementar o mesmo app que fizemos utilizando o React Navigation.
Implementação com Expo Router
Novamente, iremos utilizar o Expo, para inicializar o projeto execute o comando:
npx create-expo-app@latest MeuApp
Após criar o projeto, entre na pasta do projeto e digite o comando:
npm run reset-project
Recomendo que você renomeie os arquivos index e layout para o formato .jsx
, porque nesse projeto não iremos utilizar Typescript, também faremos a criação de uma pasta de screens que será responsável por armazenar as telas do nosso app
.
Aqui no Expo Router criamos apenas a tela de detalhes e usar o index como home, mas fique à vontade para criar uma outra tela.
Nosso app ficará com a seguinte estrutura de pastas:
MeuApp/
├── app/
│ ├── _layout.jsx
│ ├── index.jsx
│ └── screens/
│ ├── details_screen.jsx
O arquivo _layout.jsx
é responsável por definir o layout e a navegação do aplicativo. Ele organiza a estrutura da página, como cabeçalhos, menus e rodapés, e gerencia como a pessoa usuária navega entre as telas.
Fazendo com que o visual e a navegação sejam sempre os mesmos, sem precisar configurar tudo em cada página, além de facilitar a manutenção do app, pois qualquer mudança no design ou na navegação pode ser feita de uma vez, de forma centralizada.
No nosso app, o arquivo _layout.jsx
irá ficar da seguinte maneira:
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: "Home" }} />
<Stack.Screen name="screens/details_screen" options={{ title: "Detalhes" }} />
</Stack>
);
}
Na tela inicial (index.js
), é possível criar links para as telas de detalhes. Diferente do React Navigation, aqui utilizamos o componente Link
do Expo Router para navegar entre as telas, funcionando de maneira semelhante a links em uma página da web. Agora, vamos criar a tela de detalhes.
import { Link } from "expo-router";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
export default function Index() {
const users = [
{ id: 1, name: 'Alice', email: '[email protected]' },
{ id: 2, name: 'Bob', email: '[email protected]' },
{ id: 3, name: 'Charlie', email: '[email protected]' },
{ id: 4, name: 'David', email: '[email protected]' },
];
return (
<View style={styles.container}>
{users.map((user) => (
<Link
key={user.id}
href={`/screens/details_screen?id=${user.id}&email=${user.email}`}
asChild
>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>Ver detalhes de {user.name}</Text>
</TouchableOpacity>
</Link>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F8F9FA',
padding: 20,
},
button: {
backgroundColor: '#007BFF',
width: '80%',
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 3,
elevation: 3,
},
buttonText: {
color: '#FFF',
fontSize: 16,
fontWeight: 'bold',
},
});
A tela details_screen.jsx
recebe o ID e o email como parâmetros utilizando useLocalSearchParams
e exibe as informações correspondentes.
import { View, Text, StyleSheet } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
export default function DetailsScreen() {
const { id, email } = useLocalSearchParams();
return (
<View style={styles.container}>
<Text style={styles.title}>Detalhes</Text>
<Text style={styles.info}>ID: {id}</Text>
<Text style={styles.info}>Email: {email}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFF',
padding: 20,
},
title: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 12,
color: '#333',
},
info: {
fontSize: 18,
marginBottom: 8,
color: '#555',
},
});
Gostou do resultado? O Expo facilita tudo, não é mesmo?
Caso queira abrir em um dispositivo android deve utilizar um dispositivo físico ou ter um emulador instalado e executando.
Para abrir seu projeto, utilize o:
npx expo start
Este será o resultado final do nosso app:

Neste momento, depois de utilizarmos tanto o React Navigation quanto o Expo Router, surge uma grande dúvida: qual escolher?
React Navigation ou Expo Router?
Para resolver de vez essa dúvida "cruel", vamos analisar o exemplo a seguir:
import { View, Text, Button, Animated } from 'react-native';
import { useRouter } from 'expo-router';
import { useEffect, useRef } from 'react';
export default function Home() {
const router = useRouter();
const fadeAnim = useRef(new Animated.Value(0)).current; //opacidade
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
}, []);
return (
<Animated.View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', opacity: fadeAnim }}>
<Text>Home Screen</Text>
<Button
title="Ir para perfil"
onPress={() => {
alert('Navegação de exemplo');
}}
/>
</Animated.View>
);
}
Adicionamos uma animação de fade-in para mostrar que o Expo também suporta animações.
Porém, as transições são mais limitadas, com poucas opções de personalização e, dependendo do que você precisa para o seu app, o Expo Router pode não oferecer toda a flexibilidade necessária.
Já no React Navigation é diferente, ele permite ter quase total controle na criação de animações, como no exemplo a seguir:
import { View, Text, Button, Animated } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useEffect, useRef } from 'react';
export default function HomeScreen() {
const navigation = useNavigation();
const fadeAnim = useRef(new Animated.Value(0)).current; // Opacidade
const scaleAnim = useRef(new Animated.Value(0.8)).current; // Escala
useEffect(() => {
// Animação de fade-in e escala
Animated.sequence([
Animated.timing(fadeAnim, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}),
Animated.timing(scaleAnim, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}),
]).start();
}, []);
return (
<Animated.View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
opacity: fadeAnim,
transform: [{ scale: scaleAnim }],
}}
>
<Text>Home Screen</Text>
<Button
title="Ir para o Perfil"
onPress={() => {
alert('Navegação de exemplo');
}}
/>
</Animated.View>
);
}
Neste código, criamos uma tela inicial (HomeScreen
) com animações de fade-in e escala utilizando o Animated
do React Native. Ao carregar a tela, a opacidade passa de 0 a 1 e a escala de 0.8 a 1, criando uma transição suave.
Viu só? Com React Navigation, podemos, de fato, montar uma animação exatamente como quisermos, como se fosse um quebra-cabeça.
Claro, aqui exploramos principalmente as animações, mas esse ganho em personalização do React Navigation está presente também em outras etapas que compõem a estrutura de navegação.
"Ah, já sei, devo sempre usar React Navigation!"
Calma! Aqui mostramos algumas limitações do Expo Router, mas também vimos o quanto ele simplifica a configuração da navegação.
No final, a decisão vai depender do contexto e das necessidades do seu projeto! Se está começando agora e quer evitar configurações complexas, o Expo Router pode ser um ótimo ponto de partida. Se precisar de mais controle sobre animações e outros aspectos que compõem a navegação do seu app, o React Navigation será mais adequado.
Conclusão
Este artigo tem como principal referência as documentações de cada uma dessas tecnologias:
Independente da sua escolha, a Alura está aqui para te ajudar a dar o próximo passo, confira nossa formação de React Native:
Um abraço, dev! Até a próxima!