![]()
Ở bài trước, bạn đã nắm được Stack Navigation - "nghệ thuật" mở các trang theo dạng xếp chồng lên nhau.
Tuy nhiên, hãy nhìn vào ứng dụng Facebook, Shopee hay Tiktok trên điện thoại của bạn. Để chuyển đổi giữa "Trang chủ", "Thông báo" và "Tài khoản", bạn không thể cứ dùng Stack để đẩy trang lên rồi lại bấm nút Back mãi được. Bạn cần một thanh Menu cố định luôn xuất hiện trên màn hình!
Trong lập trình Mobile, có 2 loại Menu phổ biến nhất:
- Bottom Tabs: Thanh điều hướng nằm dưới đáy màn hình.
- Drawer: Menu ẩn vuốt từ cạnh viền (thường gọi là Hamburger menu).
Hôm nay, chúng ta sẽ dùng "sức mạnh" của Expo Router để tạo ra cả hai loại Menu này một cách vô cùng dễ dàng.
1. Bí mật đằng sau thanh Menu: _layout.tsx
Trước khi bắt tay vào code, bạn phải hiểu rõ một quy tắc sống còn của Expo Router: File-based routing.
Bạn có nhớ ở những bài trước đã nhắc đến một file có tên đặc biệt là _layout.tsx không?
- Trong Expo Router,
_layout.tsxđóng vai trò là một khung xương (Wrapper). - Thay vì chỉ hiển thị một màn hình duy nhất, file này có nhiệm vụ "ôm" lấy tất cả các màn hình nằm cùng thư mục với nó và gắn thêm một bộ khung chung (như thanh Header dùng chung, hoặc thanh Bottom Tabs dùng chung).
Để tạo Bottom Tabs, chúng ta sẽ sử dụng file _layout.tsx!
2. Dựng nhanh Bottom Tabs trong 5 phút
Bottom Tabs là tiêu chuẩn vàng của thiết kế Mobile hiện đại (UX/UI) vì nó nằm ngay trong tầm với của ngón cái.

Để tạo Tabs gọn gàng, trong thư mục app/, chúng ta thường nhóm các màn hình Tab vào một thư mục có ngoặc đơn, ví dụ: app/(tabs).
(Lưu ý: Thư mục có dấu ngoặc đơn () gọi là Route Group, nó giúp gom nhóm file mà KHÔNG làm thay đổi đường dẫn URL).
Cấu trúc dự án lúc này sẽ trông như sau:
📁 app
└── 📁 (tabs)
├── 📄 _layout.tsx <-- Trái tim của Bottom Tabs
├── 📄 index.tsx <-- Tab Trang chủ (Home)
└── 📄 profile.tsx <-- Tab Cá nhân (Profile)
Bây giờ, hãy mở file app/(tabs)/_layout.tsx và nhập đoạn code sau:
import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons' // Thư viện icon có sẵn của Expo
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: '#e91e63' }}>
{/* Định nghĩa Tab Trang Chủ */}
<Tabs.Screen
name="index" // Trỏ tới file index.tsx
options={{
title: 'Trang Chủ',
tabBarIcon: ({ color }) => <Ionicons name="home" size={24} color={color} />,
}}
/>
{/* Định nghĩa Tab Cá Nhân */}
<Tabs.Screen
name="profile" // Trỏ tới file profile.tsx
options={{
title: 'Cá Nhân',
tabBarIcon: ({ color }) => <Ionicons name="person" size={24} color={color} />,
}}
/>
</Tabs>
)
}
Giải mã đoạn code:
- Component
<Tabs>được lấy thẳng từexpo-router. Nó sẽ tự động biến các file nằm cạnh nó thành một thanh menu dưới đáy. name="index"vàname="profile"chính là sự kết nối giữa cấu trúc Tab với các file giao diện của bạn.tabBarIconlà nơi bạn gắn các icon đẹp mắt. Expo đã tích hợp sẵn thư viện khổng lồ@expo/vector-icons, bạn không cần cài thêm bất cứ thứ gì!
Chỉ với vài dòng code trên, bạn đã có một thanh Bottom Tabs chuẩn xác, mượt mà và tự động tô màu khi được chọn!
3. Tạo Menu vuốt từ cạnh bên (Drawer Navigation)
Drawer Navigation thường được sử dụng khi ứng dụng của bạn có quá nhiều mục (như Cài đặt, Liên hệ, Trợ giúp, Điều khoản...) mà thanh Bottom Tabs (tối đa 5 mục) không thể chứa hết.

Khác với Tabs có sẵn, để dùng Drawer, bạn cần cài đặt thêm 2 thư viện hỗ trợ cử chỉ vuốt cực mạnh của React Native:
Bước 1: Mở Terminal và chạy lệnh cài đặt:
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
Bước 2: Cấu trúc code
Cấu trúc hoàn toàn tương tự như Tabs. Bạn tạo một thư mục app/(drawer) và thêm file _layout.tsx vào đó:
// app/(drawer)/_layout.tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler'
import { Drawer } from 'expo-router/drawer'
import { Ionicons } from '@expo/vector-icons'
export default function DrawerLayout() {
return (
// Bắt buộc phải bọc Drawer trong GestureHandlerRootView để nhận diện thao tác vuốt
<GestureHandlerRootView style={{ flex: 1 }}>
<Drawer screenOptions={{ drawerActiveTintColor: '#0984e3' }}>
<Drawer.Screen
name="index" // Trỏ tới file index.tsx (Trang chính của Drawer)
options={{
drawerLabel: 'Trang Chủ',
title: 'Trang Chủ',
drawerIcon: ({ color }) => <Ionicons name="home-outline" size={24} color={color} />,
}}
/>
<Drawer.Screen
name="settings" // Trỏ tới file settings.tsx
options={{
drawerLabel: 'Cài Đặt',
title: 'Hệ Thống Cài Đặt',
drawerIcon: ({ color }) => <Ionicons name="settings-outline" size={24} color={color} />,
}}
/>
</Drawer>
</GestureHandlerRootView>
)
}
Lúc này, trên cùng góc trái màn hình của bạn sẽ xuất hiện một icon "3 dấu gạch ngang" (Hamburger). Khi bấm vào hoặc dùng tay vuốt từ mép trái màn hình sang, một bảng Menu tuyệt đẹp sẽ trượt ra!
4. Kiến trúc thực tế: Kết hợp cả 3 loại Navigation
Bạn có thể thắc mắc: "Vậy tôi nên dùng Stack, Tabs hay Drawer?"
Câu trả lời của các ứng dụng hàng đầu (như Facebook) là: Dùng cả 3! Chúng được lồng ghép vào nhau theo một kiến trúc hình cây cực kỳ thông minh:
- Tầng 1 (Root Stack): Gồm 2 khu vực lớn là
[Màn hình Đăng Nhập]và[Khu vực Đã Đăng Nhập]. (Nếu chưa đăng nhập thì không cho xem Tabs). - Tầng 2 (Bên trong Khu vực Đã Đăng Nhập): Chứa thanh Bottom Tabs để chuyển đổi giữa Trang Chủ, Video, Bạn bè, Tài khoản.
- Tầng 3 (Bên trong Tab Tài khoản): Có thể chứa một Drawer để kéo ra các tùy chọn Cài đặt bảo mật, Trợ giúp, Đăng xuất.
- Tầng 4 (Bên trong Tab Trang Chủ): Khi bấm vào một bài viết, ứng dụng dùng Stack để mở đè trang Chi Tiết Bài Viết lên trên cùng.
Với Expo Router, việc lồng ghép này cực kỳ đơn giản. Bạn chỉ cần xếp các thư mục lồng vào nhau, hệ thống file-based routing sẽ tự động hiểu và sắp xếp luồng đi cho bạn!
Kết luận: Navigation đã không còn là trở ngại
Chúc mừng bạn! Kết thúc bài học, bạn đã chính thức nắm trong tay bộ 3 "quyền lực" nhất của điều hướng: Stack, Tabs và Drawer. Hiện tại, ứng dụng của bạn đã có một bộ khung hoàn hảo, giao diện lộng lẫy và trải nghiệm chuyển trang mượt mà không thua kém bất kỳ ứng dụng nào trên Store.
Tuy nhiên, mọi dữ liệu từ đầu series đến giờ đều là dữ liệu "chết" (do chúng ta tự gõ tay vào code). Ở thế giới thực, thông tin bài viết, tài khoản hay bình luận đều phải được lấy từ server.
Hãy chuẩn bị tinh thần bước vào giai đoạn tiếp theo, chúng ta sẽ cùng nhau mang sức sống vào ứng dụng thông qua API.