ClaudeExpoJul 11, 2026, 2:30 PM

Building balanced mobile layouts: nesting tabs and drawers with Expo Router

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

claudeen

Building balanced mobile layouts: nesting tabs and drawers with Expo Router Summary

Key Points

  • Point 1: You've got a bottom tab bar working.
  • Point 2: Then a mockup lands with a hamburger menu that slides out over the tabs: a workspace switcher, settings, a link to support.
  • Point 3: Now you need both navigation patterns at once, one nested inside the other, without the drawer swipe fighting your image carousel or the tab state resetting every time someone open

Summary

This is an English summary of "Building balanced mobile layouts: nesting tabs and drawers with Expo Router" published on 2026-07-11.

Key Points

  • Point 1: You've got a bottom tab bar working.
  • Point 2: Then a mockup lands with a hamburger menu that slides out over the tabs: a workspace switcher, settings, a link to support.
  • Point 3: Now you need both navigation patterns at once, one nested inside the other, without the drawer swipe fighting your image carousel or the tab state resetting every time someone open

Full Translation

Translations

A translation section that keeps the flow of the original article.

claudeja

Building balanced mobile layouts: nesting tabs and drawers with Expo Router(原文タイトル)

概要

公開日: 2026-07-11 翻訳生成に失敗したため、原文をそのまま保存しています。

原文

You've got a bottom tab bar working. Then a mockup lands with a hamburger menu that slides out over the tabs: a workspace switcher, settings, a link to support. Now you need both navigation patterns at once, one nested inside the other, without the drawer swipe fighting your image carousel or the tab state resetting every time someone opens the menu. A persistent bottom tab bar paired with a global sidebar drawer is a common pattern in cross-platform apps. Nest those navigation trees carelessly, though, and you invite performance overhead and gesture conflicts. With Expo Router, you map these flows directly onto a file-system directory. Your code stays modular and maintainable, and the native navigation components still do the work underneath. I shared a demo of this on twitter and the Expo team reached out about sharing my process. Hopefully by the end of this post you'll have a drawer wrapping a tab navigator, plus the theming and gesture handling that keep the whole thing from feeling janky. Architectural fit: when to nest drawers and tabs Combining navigation patterns is powerful, but it shouldn't be your default. Before writing any code, work out whether your app's UX actually benefits from a dual-navigation hierarchy. Three rough cases: - Flat navigation (3 to 5 high-level screens with isolated paths): use pure bottom tabs. A side drawer here just adds cognitive load and visual clutter. - Contextual isolation (a multi-tab workspace that needs omnipresent utilities like org switching or support): nest the tab navigator inside an outer drawer container. - Deep-linked workflows (transaction-heavy dashboards where users bounce between deep sub-routes): reach for native stack navigation with context-driven visibility instead. Architectural tradeoffs and limitations Clean file-system layout paths ease structural complexity, but a few constraints are worth knowing about: - State synchronization: Passing shared global state or active user details from a standalone drawer route (like a detached settings.tsx) down into the nested tab context takes deliberate context scoping, or an external store. - Platform gesture conflicts: Side-swipe drawer gestures can conflict with horizontal elements inside your tabs, like swipeable carousels or swipe-to-delete lists. Restricting swipeEdgeWidth keeps those interactions predictable. The core architecture To keep navigation state unified, your project directory should mirror your layout tree. Instead of a centralized routing config file, Expo Router lets you declare the view hierarchy directly with folder grouping semantics like (drawer) and (tabs). app/ ├── (drawer)/ │ ├── _layout.tsx # Outermost navigation wrapper (configures side drawer) │ ├── (tabs)/ │ │ ├── _layout.tsx # Inner layout (configures persistent bottom tabs) │ │ ├── index.tsx # Home dashboard screen │ │ ├── explore.tsx # Explore feed screen │ │ ├── notifications.tsx │ │ └── profile.tsx # User profile screen │ └── settings.tsx # Standalone global route accessed via drawer └── _layout.tsx # Root orchestration and app lifecycle entry point 1. Root orchestration (app/_layout.tsx) The root file is where the app lifecycle starts. It manages the native splash screen and sets up global context providers, so runtime configuration is in place before any child route renders. import 'react-native-gesture-handler'; import React, { useState } from 'react'; import { Slot } from 'expo-router'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { StyleSheet } from 'react-native'; import * as ExpoSplashScreen from 'expo-splash-screen'; import { ThemeProvider } from '../src/context/ThemeContext'; import SplashScreenView from '../src/screens/SplashScreen'; // Prevent splash screen from auto-hiding before initialization checks finish ExpoSplashScreen.preventAutoHideAsync(); export default function RootLayout() { const [appIsReady, setAppIsReady] = useState