Pylarion Logo Pylarion Logo Pylarion
MOBILE DEVELOPMENT
12 MIN READ

Integración de Drawer y Bottom Tabs en Expo Router

Pylarion Pylarion

Pylarion

Equipo de Pylarion. Construimos software confiable donde la tecnología no puede fallar. • 12 de julio de 2026

Integración de Drawer y Bottom Tabs en Expo Router

Integración de Drawer y Bottom Tabs en Expo Router: Arquitectura de Navegación Avanzada en React Native

El desarrollo de aplicaciones móviles modernas exige patrones de navegación que combinen fluidez, mantenibilidad y una experiencia de usuario impecable. Expo Router, construido sobre React Navigation, introduce el paradigma de file-system routing que permite estructurar las rutas de una aplicación directamente desde la jerarquía de carpetas del proyecto. Cuando se trata de integrar dos de los patrones de navegación más utilizados —el drawer lateral y las pestañas inferiores— la correcta orquestación entre ambos se convierte en un desafío técnico que requiere comprensión profunda tanto del sistema de archivos como del manejo de gestos táctiles.

La combinación de un Drawer Navigator que envuelve un Bottom Tab Navigator representa uno de los patrones más demandantes en arquitecturas de navegación móvil. El drawer actúa como contenedor de primer nivel, mientras que las pestañas inferiores operan como subrutas dentro de cada sección principal. Este anidamiento genera una jerarquía clara de navegación que, si no se gestiona correctamente, puede derivar en conflictos de gestos donde el deslizamiento horizontal para abrir el cajón lateral interfiere con interacciones esperadas dentro de las pantallas de las pestañas.

Estructura de Archivos con File-System Routing

Expo Router adopta el modelo de enrutamiento basado en el sistema de archivos inspirado en Next.js, trasladando esta filosofía al ecosistema móvil. La organización del proyecto define directamente la jerarquía de navegación, eliminando la necesidad de registrar rutas manualmente. Para implementar el patrón drawer + tabs, la estructura de directorios debe reflejar con precisión el árbol de navegación deseado.

Una arquitectura recomendada para este patrón implica crear un layout raíz que instancie el Drawer Navigator, y dentro de este, un subdirectorio dedicado a las pantallas con pestañas inferiores. Cada carpeta con un archivo _layout.tsx representa un nivel de navegación independiente, lo que facilita la separación de responsabilidades y el mantenimiento a largo plazo del proyecto.

app/
├── _layout.tsx          ← Drawer Navigator (raíz)
├── (drawer)/
│   ├── _layout.tsx      ← Configuración del Drawer
│   ├── (tabs)/
│   │   ├── _layout.tsx  ← Bottom Tab Navigator
│   │   ├── index.tsx    ← Tab: Inicio
│   │   ├── explore.tsx  ← Tab: Explorar
│   │   └── profile.tsx  ← Tab: Perfil
│   ├── settings.tsx     ← Pantalla del Drawer
│   └── about.tsx        ← Pantalla del Drawer

Esta disposición garantiza que el enrutador interprete correctamente la jerarquía de navegación sin ambigüedades. El grupo (drawer) encapsula tanto las pantallas accesibles exclusivamente desde el menú lateral como el grupo (tabs), que contiene las rutas navegables mediante las pestañas inferiores. Los paréntesis en los nombres de carpeta son una convención de Expo Router para crear grupos de rutas sin afectar la URL pública de las mismas.

Configuración del Drawer Navigator

La implementación del Drawer Navigator en el archivo (drawer)/_layout.tsx requiere la instalación y configuración de las dependencias específicas del ecosistema React Navigation. La librería @react-navigation/drawer junto con react-native-gesture-handler y react-native-reanimated conforman el núcleo técnico de este componente de navegación.

// app/(drawer)/_layout.tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { Drawer } from 'expo-router/drawer';

export default function DrawerLayout() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <Drawer
        screenOptions={{
          headerShown: true,
          drawerType: 'front',
          swipeEdgeWidth: 50,
          drawerStyle: {
            backgroundColor: '#1a1a2e',
            width: 280,
          },
        }}
      >
        <Drawer.Screen
          name="(tabs)"
          options={{
            title: 'Inicio',
            drawerLabel: 'Panel Principal',
          }}
        />
        <Drawer.Screen
          name="settings"
          options={{ title: 'Configuración' }}
        />
        <Drawer.Screen
          name="about"
          options={{ title: 'Acerca de' }}
        />
      </Drawer>
    </GestureHandlerRootView>
  );
}

El parámetro swipeEdgeWidth es crítico para resolver uno de los conflictos más comunes en esta arquitectura. Al limitar la zona de activación del gesto de apertura del drawer a un margen específico desde el borde izquierdo de la pantalla, se previene que el deslizamiento horizontal dentro de componentes como carruseles, listas horizontales o swipeable items dentro de las pestañas active involuntariamente el cajón lateral. El valor de 50 representa un equilibrio razonable entre accesibilidad y no interferencia gestual.

Implementación del Bottom Tab Navigator

El archivo (tabs)/_layout.tsx define la configuración de las pestañas inferiores. En este nivel es donde se establece la tematización visual coherente con el drawer y se configuran los íconos, colores activos y comportamientos específicos de cada pestaña. Expo Router expone el componente Tabs que abstrae la configuración del Tab Navigator de React Navigation.

// app/(drawer)/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { useColorScheme } from 'react-native';
import { Ionicons } from '@expo/vector-icons';

const PRIMARY_COLOR = '#e94560';
const INACTIVE_COLOR = '#8892b0';

export default function TabsLayout() {
  const colorScheme = useColorScheme();
  const isDark = colorScheme === 'dark';

  return (
    <Tabs
      screenOptions={{
        headerShown: false,
        tabBarActiveTintColor: PRIMARY_COLOR,
        tabBarInactiveTintColor: INACTIVE_COLOR,
        tabBarStyle: {
          backgroundColor: isDark ? '#0f3460' : '#ffffff',
          borderTopColor: isDark ? '#1a1a2e' : '#e0e0e0',
          height: 60,
          paddingBottom: 8,
        },
        tabBarLabelStyle: {
          fontSize: 11,
          fontWeight: '600',
        },
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: 'Inicio',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="home-outline" size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="explore"
        options={{
          title: 'Explorar',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="compass-outline" size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="profile"
        options={{
          title: 'Perfil',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="person-outline" size={size} color={color} />
          ),
        }}
      />
    </Tabs>
  );
}

La decisión de establecer headerShown: false en el nivel de las pestañas responde a una consideración de UX: el header principal es gestionado por el Drawer Navigator, que ya incorpora el botón de hamburguesa para abrir el menú lateral. Duplicar el header generaría redundancia visual y consumo innecesario de espacio en pantalla. Esta delegación de responsabilidades entre navegadores es una práctica fundamental en la arquitectura anidada.

Manejo Avanzado de Gestos y Resolución de Conflictos

El desafío más significativo de esta arquitectura reside en la coexistencia de múltiples reconocedores de gestos sobre la misma superficie táctil. React Native Gesture Handler implementa un sistema de árbol de gestos donde los reconocedores compiten por la prioridad de activación. Cuando el drawer intenta capturar deslizamientos horizontales al mismo tiempo que un componente interno —como un FlatList horizontal o un Swipeable— también reclama ese gesto, se produce una colisión que degrada la experiencia de usuario.

// Componente con gesto interno que debe tener prioridad sobre el Drawer
import { ScrollView } from 'react-native-gesture-handler';
import { useNavigation } from 'expo-router';
import { DrawerNavigationProp } from '@react-navigation/drawer';

export default function ExploreScreen() {
  const navigation = useNavigation<DrawerNavigationProp<any>>();

  const handleScrollBeginDrag = () => {
    // Desactiva el gesto del drawer durante el scroll horizontal
    navigation.getParent('drawer')?.setOptions({
      swipeEnabled: false,
    });
  };

  const handleScrollEndDrag = () => {
    // Reactiva el gesto del drawer al finalizar
    navigation.getParent('drawer')?.setOptions({
      swipeEnabled: true,
    });
  };

  return (
    <ScrollView
      horizontal
      onScrollBeginDrag={handleScrollBeginDrag}
      onScrollEndDrag={handleScrollEndDrag}
    >
      {/* contenido horizontal */}
    </ScrollView>
  );
}

Este patrón de activación/desactivación dinámica del gesto del drawer es la solución más robusta para entornos donde las pantallas de las pestañas contienen interacciones horizontales propias. Alternativamente, se puede recurrir a la configuración de gestureHandlerProps en el Drawer para definir directivas de hitSlop que limiten con mayor precisión la zona reactiva, o implementar handlers personalizados con la API de gestos simultáneos de RNGH v2.

Tematización Coherente entre Drawer y Tabs

La consistencia visual entre el cajón lateral y las pestañas inferiores es un requisito no funcional que impacta directamente en la percepción de calidad de la aplicación. Expo Router se integra nativamente con el sistema de temas de React Navigation, permitiendo la definición de un tema global que se propaga automáticamente a todos los navegadores anidados.

// app/_layout.tsx (layout raíz)
import { ThemeProvider, DarkTheme, DefaultTheme } from '@react-navigation/native';
import { useColorScheme } from 'react-native';
import { Stack } from 'expo-router';

const CustomLightTheme = {
  ...DefaultTheme,
  colors: {
    ...DefaultTheme.colors,
    primary: '#e94560',
    background: '#ffffff',
    card: '#f8f9fa',
    text: '#1a1a2e',
    border: '#e0e0e0',
    notification: '#e94560',
  },
};

const CustomDarkTheme = {
  ...DarkTheme,
  colors: {
    ...DarkTheme.colors,
    primary: '#e94560',
    background: '#0f3460',
    card: '#1a1a2e',
    text: '#ccd6f6',
    border: '#233554',
    notification: '#e94560',
  },
};

export default function RootLayout() {
  const colorScheme = useColorScheme();

  return (
    <ThemeProvider value={colorScheme === 'dark' ? CustomDarkTheme : CustomLightTheme}>
      <Stack screenOptions={{ headerShown: false }} />
    </ThemeProvider>
  );
}

La propagación del tema a través del árbol de navegación garantiza que tanto el drawer como las pestañas consuman los mismos valores de color sin necesidad de redefinirlos en cada nivel. Los componentes nativos de React Navigation leen automáticamente el contexto del tema para aplicar colores de fondo, texto y bordes, lo que reduce significativamente la superficie de configuración manual y elimina inconsistencias visuales entre navegadores.

Consideraciones de Rendimiento y Buenas Prácticas

La arquitectura de navegación anidada introduce un overhead de renderizado que debe ser monitoreado en producción. Cada navegador activo mantiene en memoria sus pantallas y estados de navegación, lo que puede incrementar el consumo de memoria en dispositivos de gama baja. Expo Router ofrece la propiedad lazy en el Tab Navigator para diferir el renderizado de pestañas no visitadas, y el Drawer Navigator soporta opciones similares de inicialización diferida para pantallas secundarias.

  • Utilizar lazy={true} en el Tab Navigator para renderizar pestañas solo cuando sean visitadas por primera vez.
  • Implementar React.memo en componentes de pantalla que no dependan de cambios frecuentes de estado.
  • Evitar subscribirse a stores globales de estado en los layouts de navegación; preferir pasar callbacks o usar contextos locales.
  • Configurar detachInactiveScreens={true} en el Drawer para desmontar pantallas inactivas y liberar recursos.
  • Medir el tiempo de interacción con herramientas como Flipper o las DevTools de React Native para identificar cuellos de botella específicos del navegador.

"La navegación no es solo un detalle de implementación; es la columna vertebral de la arquitectura de una aplicación móvil. Una jerarquía de navegación bien diseñada reduce la deuda técnica, mejora la mantenibilidad y entrega una experiencia de usuario que se percibe como nativa y profesional."

La integración de Drawer y Bottom Tabs en Expo Router representa un patrón maduro y escalable para aplicaciones React Native de complejidad media-alta. El aprovechamiento del file-system routing no solo simplifica la configuración inicial, sino que establece convenciones claras para el crecimiento del proyecto. La clave del éxito en esta arquitectura radica en comprender los límites de responsabilidad de cada navegador, gestionar los conflictos de gestos con precisión quirúrgica y mantener una tematización coherente que transmita unidad visual al usuario final. Con las configuraciones descritas, es posible construir aplicaciones que compitan en calidad de experiencia con las desarrolladas mediante herramientas nativas.

2
Pylarion Pylarion

Pylarion

Mobile Development Expert

Equipo de Pylarion. Construimos software confiable donde la tecnología no puede fallar.

Blog / Mobile Development / Integración de Drawer y Bottom Tabs en Expo Router

Comentarios

(0)
Categoría: Mobile Development

Artículos Recomendados

Lanzamiento de SDK 56: expo-widgets alcanza la versión estable
Mobile Development
5
0
0

Lanzamiento de SDK 56: expo-widgets alcanza la versión estable

La reciente actualización a Expo SDK 56 ha marcado un hito importante para los desarrolladores de aplicaciones móviles al promover la biblioteca expo-widgets a su versión estable. Esta importante novedad permite a los programadores construir y diseñar widgets personalizados para la pantalla de inicio, así como Live Activities para la pantalla de bloqueo de los dispositivos, utilizando exclusivamente componentes de React. El aspecto más destacado de esta actualización es que elimina por completo la necesidad de tener conocimientos o escribir código nativo en SwiftUI para lograr la integración con el sistema operativo de Apple. Gracias a esta mejora, los equipos de desarrollo podrán optimizar notablemente sus tiempos de entrega, unificar sus bases de código bajo el ecosistema de React Native y ofrecer experiencias de usuario más dinámicas y nativas en iOS sin salir de su entorno de trabajo habitual, facilitando en gran medida la creación de funcionalidades avanzadas e interactivas.

Leer artículo
Expo Widgets estable en SDK 56
Mobile Development
7
0
0

Expo Widgets estable en SDK 56

La nueva actualización a SDK 56 ha promovido la herramienta expo-widgets a su versión estable, marcando un avance significativo para los desarrolladores. Ahora es posible construir widgets para la pantalla de inicio y Live Activities para la pantalla de bloqueo utilizando exclusivamente componentes de React. Esta innovación elimina por completo la necesidad de programar en SwiftUI o utilizar código nativo de Apple. Como resultado, los equipos de programación pueden mantener su flujo de trabajo basado en React Native para ofrecer experiencias ricas y nativas en iOS. Esta mejora no solo optimiza el tiempo de desarrollo, sino que también reduce la complejidad técnica y la curva de aprendizaje, unificando la creación de interfaces tanto para la aplicación principal como para las extensiones del sistema en una sola base de código eficiente.

Leer artículo
Expo SDK 56: expo-widgets alcanza la versión estable
Mobile Development
1
0
0

Expo SDK 56: expo-widgets alcanza la versión estable

La versión estable de expo-widgets ha sido lanzada oficialmente con la llegada de Expo SDK 56. Esta actualización facilita enormemente el trabajo de los desarrolladores de aplicaciones móviles, permitiendo construir widgets nativos para la pantalla de inicio y Live Activities para la pantalla de bloqueo utilizando únicamente componentes de React. El principal beneficio de esta herramienta es que elimina por completo la necesidad de tener conocimientos previos o escribir código nativo en SwiftUI. De esta forma, se unifica el proceso de creación dentro del ecosistema de React Native, lo que reduce los tiempos de desarrollo, disminuye la barrera de entrada para la implementación de funciones nativas avanzadas en iOS y mejora significativamente la productividad de los equipos enfocados en el ecosistema móvil.

Leer artículo