Abas & Gavetas
Barras de abas, gestos de gaveta e composição de layout aninhado. Tabs e Drawer do expo-router envolvem navegadores do React Navigation - nomes de arquivos sob a pasta de layout se tornam destinos automaticamente.
Busque em todas as páginas da documentação
Barras de abas, gestos de gaveta e composição de layout aninhado. Tabs e Drawer do expo-router envolvem navegadores do React Navigation - nomes de arquivos sob a pasta de layout se tornam destinos automaticamente.
Cartão de receita de referência rápida - pronto para copiar e colar.
npx expo install expo-router
# Apenas Gaveta:
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimatedapp/
├── _layout.tsx
└── (tabs)/
├── _layout.tsx → Abas
├── index.tsx → Aba Home
├── explore.tsx → Aba Explore
└── orders/
├── _layout.tsx → Stack aninhado na aba Orders
├── index.tsx
└── [id].tsx// app/(tabs)/_layout.tsx
import { Tabs } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: "#2563eb",
headerShown: true,
}}
>
<Tabs.Screen
name="index"
options={{
title: "Home",
tabBarIcon: ({ color, size }) => <Ionicons name="home" size={size} color={color} />,
}}
/>
<Tabs.Screen
name="explore"
options={{
title: "Explore",
tabBarIcon: ({ color, size }) => <Ionicons name="compass" size={size} color={color} />,
}}
/>
<Tabs.Screen
name="orders"
options={{
title: "Orders",
headerShown: false,
tabBarIcon: ({ color, size }) => <Ionicons name="list" size={size} color={color} />,
}}
/>
</Tabs>
);
}Quando usar isso:
Abas com um stack de pedidos aninhado e um shell de gaveta opcional para navegação mais ampla.
// app/_layout.tsx
import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(drawer)" />
<Stack.Screen name="(auth)" options={{ presentation: "modal" }} />
</Stack>
);
}// app/(drawer)/_layout.tsx
import { Drawer } from "expo-router/drawer";
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function DrawerLayout() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Drawer screenOptions={{ headerShown: false }}>
<Drawer.Screen name="(tabs)" options={{ title: "Main" }} />
<Drawer.Screen name="settings" options={{ title: "Settings" }} />
</Drawer>
</GestureHandlerRootView>
);
}// app/(drawer)/(tabs)/orders/_layout.tsx
import { Stack } from "expo-router";
export default function OrdersStack() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: "Orders" }} />
<Stack.Screen name="[id]" options={{ title: "Order Detail" }} />
</Stack>
);
}// Ocultar uma tela da barra de abas, mas mantê-la navegável
// app/(tabs)/_layout.tsx (trecho)
<Tabs.Screen
name="hidden-settings"
options={{
href: null,
title: "Hidden",
}}
/>// app/(tabs)/hidden-settings.tsx - acessível via router.push("/hidden-settings")
import { Text, View } from "react-native";
export default function HiddenSettingsScreen() {
return (
<View style={{ flex: 1, padding: 16 }}>
<Text>Não mostrado na barra de abas</Text>
</View>
);
}app/(tabs)/_layout.tsx
├── index.tsx → aba 1 (frequentemente Home)
├── explore.tsx → aba 2
└── profile.tsx → aba 3(tabs)/ se torna uma aba, a menos que href: null_layout.tsx da pasta escolhe stack/drawer dentro dessa abatabBarButton personaliza o comportamento de clique - use para abas centrais especiais FABlazy: true (padrão) adia a montagem até o primeiro foco - melhora o tempo de inicialização a frioimport { Drawer } from "expo-router/drawer";expo-router/drawer, não de expo-router - a gaveta tem dependências nativas extrasGestureHandlerRootView no layout da gaveta ou na raizdrawerPosition, drawerType e overlayColor correspondem às opções de gaveta do React Navigation| Padrão | Estrutura | Melhor para |
|---|---|---|
| Apenas Abas | (tabs)/ na raiz | Aplicativos de consumo, 3–5 seções |
| Gaveta → Abas | (drawer)/(tabs)/ | Muitas seções; abas para uso diário |
| Stack → Abas | Stack raiz exibe modais sobre abas | Modal de autenticação, composição global |
| Abas → Stack | Stack dentro de uma pasta de aba | Lista/detalhe em uma seção |
Profundidade máxima recomendada:
Stack Raiz
└── Gaveta (opcional)
└── Abas
└── Stack (por aba pesada)<Tabs
screenOptions={{
tabBarStyle: { backgroundColor: "#0f172a" },
tabBarLabelStyle: { fontSize: 12 },
tabBarHideOnKeyboard: true,
}}
/>tabBarHideOnKeyboard impede que a barra cubra campos de texto no AndroidtabBarBadge para contagens não lidas - mantenha os badges sincronizados com o estado de notificações pushTodo arquivo em (tabs)/ é exibido como aba - rotas auxiliares vazam para a barra. Correção: href: null ou mova telas que não são abas para uma pasta de stack aninhada.
Gaveta sem gesture-handler root - gestos de deslizar falham silenciosamente. Correção: GestureHandlerRootView envolvendo o layout da gaveta; certifique-se de que o plugin Babel do react-native-reanimated esteja configurado (padrão no template SDK 57).
Cabeçalhos duplos (abas + stack) - o layout da aba e o stack aninhado exibem cabeçalhos. Correção: headerShown: false nas abas para pastas que possuem um stack aninhado (padrão da aba orders).
Estado da aba sobrevive ao logout - o stack do usuário anterior permanece na memória. Correção: Remonte (tabs) com key={userId} no layout ou use router.replace para autenticação.
Link profundo para /orders/5 com a aba errada focada - o usuário chega ao detalhe, mas a aba errada é destacada. Correção: Certifique-se de que a rota esteja sob a pasta da aba correta; teste links universais por aba.
Muitas abas (6+) - a barra de abas do iOS trunca os rótulos. Correção: Mova destinos raros para a gaveta, menu de perfil ou stack "Mais".
Gaveta no iPhone como navegação principal - o alcance do polegar e a descoberta sofrem em comparação com abas inferiores. Correção: Reserve a gaveta para tablets ou 6+ destinos.
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Abas Inferiores (Tabs) | 3–5 destinos diários | IA plana com um fluxo linear |
Gaveta (Drawer) | Muitas seções, focado em tablet | Aplicativo de consumo simples com 3 abas |
Abas Superiores de Material (@react-navigation/material-top-tabs) | Seções de conteúdo deslizáveis dentro de uma área | Navegação primária em todo o aplicativo |
Stack Único + tela de hub | Aplicativos muito pequenos, assistentes | Usuários precisam de troca de seção com um toque |
Barra de abas personalizada (tabBar: () => <MyBar />) | FAB com marca, barra animada | Você só precisa de cores de realce - use screenOptions |
Barra lateral do iOS (@react-navigation/drawer permanente no iPad) | Layouts de tablet em tela dividida | MVP apenas para celular |
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimatedUse import { Drawer } from "expo-router/drawer". O template SDK 57 já inclui o plugin Babel do Reanimated.
<Tabs.Screen name="secret" options={{ href: null }} />A rota permanece navegável via router.push("/secret") ou Link href="/secret".
Sim - adicione app/(tabs)/orders/_layout.tsx com <Stack /> enquanto explore.tsx permanece uma única tela. Defina headerShown: false na entrada da aba de pedidos no layout das abas.
import { router } from "expo-router";
router.push("/(tabs)/explore");Navegue para o caminho raiz da aba. Use router.replace quando não for para preservar o histórico de volta.
<Tabs.Screen
name="create"
options={{
tabBarButton: (props) => (
<Pressable {...props} style={[props.style, styles.fab]}>
<Ionicons name="add" size={28} color="#fff" />
</Pressable>
),
}}
/>Estilize o botão personalizado - certifique-se de hit slop e rótulo de acessibilidade.
Gaveta envolvendo abas é o mais comum - gaveta para seções do aplicativo, abas para um subconjunto de alta frequência. Abas envolvendo gaveta é raro e confunde o comportamento de retorno.
<Tabs.Screen
name="inbox"
options={{
tabBarBadge: 3,
tabBarBadgeStyle: { backgroundColor: "#ef4444" },
}}
/>Atualize os valores dos badges a partir do store de notificações ou cache de consulta - limpe ao focar na aba, se apropriado.
Sim - abas inativas permanecem montadas por padrão (lazy apenas adia a primeira montagem). Use unmountOnBlur: true nas opções da tela se a pressão de memória exigir a desmontagem de abas pesadas.
Use expo-router/testing-library renderRouter com um diretório app fictício. Afirme a troca de abas pressionando os rótulos das abas e verifique os segmentos de URL.
As abas do Expo Router usam a barra de abas JS do React Navigation por padrão. Para experimentos totalmente nativos com UITabBar, você deixaria as abas baseadas em arquivos - incomum para a maioria das equipes Expo. Prefira estilizar via screenOptions primeiro.
app/ - grupos (tabs) e (drawer)app/Versões do 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