# Expo Router trong React Native: Hướng dẫn đầy đủ về định tuyến dựa trên file
> Expo Router mang hệ thống định tuyến dựa trên file đến React Native, lấy cảm hứng từ Next.js. Hướng dẫn toàn diện với cấu hình, tab navigation, dynamic routes, modal, middleware và bảo vệ route.
- Published: 2026-04-18
- Updated: 2026-04-18
- Author: SharpSkill
- Tags: react-native, expo, expo-router, navigation, mobile-development, tutorial
- Reading time: 9 min
---
Expo Router đã thay đổi hoàn toàn cách quản lý điều hướng trong các ứng dụng React Native bằng việc áp dụng hệ thống định tuyến dựa trên file, lấy cảm hứng trực tiếp từ Next.js. Với SDK Expo 55 và Expo Router v6, việc cấu hình navigator thủ công đã trở thành quá khứ. Chỉ cần tạo một file trong thư mục `app`, màn hình tương ứng sẽ tự động có thể truy cập được. Android, iOS và web đều được hỗ trợ thống nhất mà không cần thêm bất kỳ cấu hình nào. Cách tiếp cận này giảm đáng kể độ phức tạp của các dự án đa nền tảng và tăng tốc quá trình phát triển ứng dụng di động.
> **Bắt đầu nhanh**
>
> Các dự án Expo mới đã tích hợp sẵn Expo Router. Lệnh `npx create-expo-app@latest --template default@sdk-55` sẽ tạo một dự án được cấu hình sẵn với hệ thống định tuyến dựa trên file. Đối với các dự án hiện có, chỉ cần cài đặt package `expo-router` và cập nhật entry point của ứng dụng.
## Cơ chế hoạt động của định tuyến dựa trên file trong Expo Router
Mỗi file được đặt trong thư mục `app` sẽ tự động trở thành một route. Đường dẫn của file tương ứng trực tiếp với đường dẫn URL, loại bỏ hoàn toàn nhu cầu sử dụng một file cấu hình tập trung. File `app/settings.tsx` tạo route `/settings`, trong khi `app/profile/edit.tsx` tương ứng với `/profile/edit`.
Quy ước này mang lại ba lợi thế quan trọng so với cách cấu hình truyền thống bằng React Navigation:
- **Không cần cấu hình**: route tồn tại ngay khi file được tạo
- **Deep linking tự động**: mỗi màn hình đều có URL riêng, thuận tiện cho việc chia sẻ và kiểm thử
- **Điều hướng có kiểu dữ liệu**: TypeScript nhận biết các route hiện có ngay tại thời điểm biên dịch
```typescript
// app/index.tsx
import { View, Text, StyleSheet } from 'react-native'
import { Link } from 'expo-router'
export default function HomeScreen() {
return (
Welcome
{/* Link maps directly to file path */}
Open Settings
Edit Profile
)
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 24 },
title: { fontSize: 28, fontWeight: 'bold', marginBottom: 16 },
link: { fontSize: 16, color: '#61DAFB', marginTop: 12 },
})
```
Component `Link` xử lý điều hướng trên tất cả các nền tảng. Trên web, nó tạo ra thẻ anchor với thuộc tính `href` phù hợp cho SEO. Trên các nền tảng native, nó kích hoạt kiểu điều hướng stack.
## Cấu trúc dự án và các file layout
Expo Router sử dụng các file `_layout.tsx` để định nghĩa các container điều hướng. Mỗi thư mục có thể chứa layout riêng của mình, tạo ra các cấu trúc điều hướng lồng nhau. Layout gốc bao bọc toàn bộ ứng dụng, trong khi các layout con kiểm soát từng phần cụ thể.
Dưới đây là cấu trúc dự án điển hình:
```text
app/
_layout.tsx # Root layout (Stack or custom)
index.tsx # Home screen (/)
(tabs)/ # Tab group (parentheses = route group)
_layout.tsx # Tab navigator
home.tsx # /home tab
search.tsx # /search tab
profile.tsx # /profile tab
settings/
_layout.tsx # Settings stack layout
index.tsx # /settings
notifications.tsx # /settings/notifications
privacy.tsx # /settings/privacy
```
Các nhóm route -- thư mục được bao bọi dấu ngoặc đơn -- giúp tổ chức file mà không ảnh hưởng đến URL. Thư mục `(tabs)` trong ví dụ trên tạo một tab navigator, nhưng các URL vẫn giữ nguyên là `/home`, `/search` và `/profile` thay vì `/tabs/home`.
```typescript
// app/_layout.tsx
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
)
}
```
Layout gốc cũng đóng vai trò là điểm khởi tạo cho việc tải font chữ, cấu hình các provider và các thiết lập toàn cục. Nó thay thế file `App.tsx` truyền thống với vai trò là entry point của ứng dụng.
## Xây dựng hệ thống điều hướng bằng tab
Điều hướng bằng tab yêu cầu một file `_layout.tsx` nằm bên trong một nhóm route. Expo Router v6 giới thiệu `NativeTabs` để mang lại trải nghiệm native đặc thù cho từng nền tảng, tuy nhiên component `Tabs` tiêu chuẩn vẫn đáp ứng được phần lớn các trường hợp sử dụng.
```typescript
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons'
export default function TabLayout() {
return (
(
),
}}
/>
(
),
}}
/>
(
),
}}
/>
)
}
```
Mỗi file màn hình tab export một React component tiêu chuẩn. Icon, nhãn và badge của thanh tab được cấu hình thông qua prop `options` trong layout.
## Route động và tham số route
Các đoạn động sử dụng dấu ngoặc vuông trong tên file. File có tên `[id].tsx` khớp với một đoạn đơn lẻ, trong khi `[...slug].tsx` bắt tất cả các đoạn còn lại của đường dẫn.
```typescript
// app/product/[id].tsx
import { View, Text, StyleSheet } from 'react-native'
import { useLocalSearchParams, Stack } from 'expo-router'
export default function ProductScreen() {
// Extract the dynamic parameter from the URL
const { id } = useLocalSearchParams<{ id: string }>()
return (
Product DetailsID: {id}
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
heading: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 },
id: { fontSize: 16, color: '#888' },
})
```
Khi truy cập `/product/42`, màn hình này sẽ hiển thị với biến `id` được gán giá trị `"42"`. Hook `useLocalSearchParams` cung cấp quyền truy cập có kiểu dữ liệu vào toàn bộ tham số route.
Đối với các route catch-all, `[...slug].tsx` bắt toàn bộ các đoạn đường dẫn:
```typescript
// app/docs/[...slug].tsx
import { useLocalSearchParams } from 'expo-router'
export default function DocsScreen() {
// /docs/getting-started/installation → slug = ['getting-started', 'installation']
const { slug } = useLocalSearchParams<{ slug: string[] }>()
return
}
```
## Kiểu dữ liệu cho route đảm bảo an toàn tại thời điểm biên dịch
Expo Router tự động sinh các kiểu dữ liệu cho route khi tính năng `typed routes` được kích hoạt. Việc kiểm tra này phát hiện các liên kết bị hỏng ngay tại thời điểm biên dịch thay vì khi chạy ứng dụng.
Kích hoạt trong file `app.json`:
```json
{
"expo": {
"experiments": {
"typedRoutes": true
}
}
}
```
Sau khi kích hoạt, prop `href` của component `Link` và tham số của `router.push()` chỉ chấp nhận các chuỗi tương ứng với route hợp lệ:
```typescript
// app/checkout.tsx
import { router } from 'expo-router'
function handleCheckout(cartId: string) {
// TypeScript validates this route exists
router.push(`/product/${cartId}`)
// This would cause a compile error if /nonexistent doesn't exist
// router.push('/nonexistent')
}
```
Tính năng typed routes phát huy hiệu quả đặc biệt khi kết hợp với `useLocalSearchParams`. Các kiểu được sinh tự động đảm bảo sự khớp giữa tên tham số trong định nghĩa route và các tham số được sử dụng bởi component, ngăn chặn những lỗi tiềm ẩn chỉ xuất hiện khi điều hướng đến một màn hình cụ thể.
## Màn hình modal và tùy chọn hiển thị
Các modal trong Expo Router là những màn hình thông thường được cấu hình với `presentation: 'modal'` trong layout. Cách tiếp cận này tuân thủ quy ước của hệ thống định tuyến dựa trên file: một modal đơn giản chỉ là một route như bất kỳ route nào khác.
```typescript
// app/_layout.tsx
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
{/* Modal screen slides up from the bottom */}
)
}
```
```typescript
// app/create-post.tsx
import { View, TextInput, Button, StyleSheet } from 'react-native'
import { router } from 'expo-router'
import { useState } from 'react'
export default function CreatePostModal() {
const [title, setTitle] = useState('')
const handleSubmit = () => {
// Submit logic here
router.back() // Dismiss the modal
}
return (
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
input: {
borderWidth: 1,
borderColor: '#333',
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 16,
},
})
```
Khi truy cập `/create-post`, màn hình modal sẽ được hiển thị. Gọi `router.back()` sẽ đóng modal và đưa người dùng quay lại màn hình trước đó trong ngăn xếp điều hướng.
## Điều hướng lập trình và API của router
Ngoài component `Link`, Expo Router cung cấp một API mệnh lệnh thông qua đối tượng `router`. API này cho phép quản lý điều hướng được kích hoạt bởi logic nghiệp vụ thay vì bởi tương tác trực tiếp của người dùng.
```typescript
import { router } from 'expo-router'
// Push a new screen onto the stack
router.push('/profile/settings')
// Replace the current screen (no back button)
router.replace('/login')
// Go back to the previous screen
router.back()
// Navigate with parameters
router.push({
pathname: '/product/[id]',
params: { id: '42', source: 'recommendations' },
})
// Check if going back is possible
import { useRouter } from 'expo-router'
function BackButton() {
const router = useRouter()
return router.canGoBack() ? (