Expo Router สำหรับ React Native: คู่มือระบบ Navigation แบบ File-Based ฉบับสมบูรณ์
คู่มือฉบับสมบูรณ์สำหรับ Expo Router ใน React Native ครอบคลุมระบบ routing แบบ file-based, การตั้งค่า layout, tab navigation, dynamic routes, modal screens, typed routes, middleware และการป้องกันเส้นทาง พร้อมตัวอย่างโค้ดจริง

Expo Router เปลี่ยนแปลงวิธีจัดการระบบ navigation ในแอปพลิเคชัน React Native อย่างสิ้นเชิง ด้วยการนำแนวคิด file-based routing ที่คุ้นเคยจาก Next.js มาปรับใช้กับโลกของ mobile development ตั้งแต่ SDK Expo 55 และ Expo Router v6 เป็นต้นมา นักพัฒนาไม่จำเป็นต้องตั้งค่า navigator ด้วยตนเองอีกต่อไป เพียงสร้างไฟล์ในไดเรกทอรี app หน้าจอนั้นก็พร้อมใช้งานทันทีทั้งบน Android, iOS และเว็บ แนวทางนี้ลดความซับซ้อนของโปรเจกต์ข้ามแพลตฟอร์มลงอย่างมาก และเร่งกระบวนการพัฒนาให้รวดเร็วยิ่งขึ้น
โปรเจกต์ Expo ใหม่ทุกโปรเจกต์มี Expo Router ติดตั้งมาให้พร้อมใช้งาน คำสั่ง npx create-expo-app@latest --template default@sdk-55 จะสร้างโปรเจกต์ที่ตั้งค่า file-based routing ไว้เรียบร้อยแล้ว สำหรับโปรเจกต์ที่มีอยู่เดิม เพียงติดตั้งแพ็กเกจ expo-router และอัปเดต entry point ของแอปพลิเคชัน
หลักการทำงานของ File-Based Routing ใน Expo Router
ไฟล์ทุกไฟล์ที่สร้างขึ้นภายในไดเรกทอรี app จะกลายเป็น route โดยอัตโนมัติ เส้นทางของไฟล์จะสอดคล้องกับเส้นทาง URL โดยตรง ทำให้ไม่จำเป็นต้องมีไฟล์ configuration แบบรวมศูนย์อีกต่อไป ตัวอย่างเช่น ไฟล์ app/settings.tsx จะสร้าง route /settings ในขณะที่ app/profile/edit.tsx จะตรงกับ /profile/edit
แนวทางนี้มีข้อได้เปรียบหลักสามประการเมื่อเทียบกับการตั้งค่า React Navigation แบบดั้งเดิม:
- ไม่ต้องตั้งค่าใดๆ -- route ถูกสร้างขึ้นทันทีที่สร้างไฟล์
- Deep linking อัตโนมัติ -- ทุกหน้าจอมี URL เป็นของตัวเอง ทำให้การแชร์ลิงก์และการทดสอบสะดวกยิ่งขึ้น
- Navigation ที่มี type safety -- TypeScript รู้จัก route ทั้งหมดที่มีอยู่ตั้งแต่ขั้นตอน compilation
import { View, Text, StyleSheet } from 'react-native'
import { Link } from 'expo-router'
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome</Text>
{/* Link maps directly to file path */}
<Link href="/settings" style={styles.link}>
Open Settings
</Link>
<Link href="/profile/edit" style={styles.link}>
Edit Profile
</Link>
</View>
)
}
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 },
})คอมโพเนนต์ Link จัดการ navigation บนทุกแพลตฟอร์ม บนเว็บจะสร้าง anchor tag พร้อม attribute href ที่เหมาะสมสำหรับ SEO ส่วนบนแพลตฟอร์ม native จะทำงานเป็น stack navigation ตามปกติ
โครงสร้างโปรเจกต์และไฟล์ Layout
Expo Router ใช้ไฟล์ _layout.tsx สำหรับกำหนด navigation container แต่ละไดเรกทอรีสามารถมี layout เป็นของตัวเอง ทำให้เกิดโครงสร้าง navigation แบบซ้อนกันได้ Layout หลักที่อยู่ระดับ root จะครอบคลุมทั้งแอปพลิเคชัน ส่วน layout ที่ซ้อนอยู่ภายในจะควบคุมเฉพาะส่วนที่เกี่ยวข้อง
โครงสร้างโปรเจกต์ตัวอย่างมีลักษณะดังนี้:
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/privacyRoute groups คือไดเรกทอรีที่ล้อมรอบด้วยวงเล็บ ซึ่งทำหน้าที่จัดระเบียบไฟล์โดยไม่ส่งผลกระทบต่อ URL ไดเรกทอรี (tabs) ในตัวอย่างด้านบนสร้าง tab navigator ขึ้นมา แต่ URL ยังคงเป็น /home, /search และ /profile ไม่ใช่ /tabs/home
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: '#1a1a2e' },
headerTintColor: '#ffffff',
headerTitleStyle: { fontWeight: '600' },
}}
>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ title: 'Settings' }} />
</Stack>
)
}Layout หลักยังทำหน้าที่เป็นจุดเริ่มต้นสำหรับการโหลดฟอนต์ การตั้งค่า provider ต่างๆ และการกำหนดค่าระดับ global อีกด้วย โดยมาแทนที่ไฟล์ App.tsx แบบดั้งเดิมในฐานะ entry point ของแอปพลิเคชัน
การสร้าง Tab Navigation
Tab navigation ต้องมีไฟล์ _layout.tsx อยู่ภายใน route group Expo Router v6 มีคอมโพเนนต์ NativeTabs สำหรับประสบการณ์ native เฉพาะแพลตฟอร์ม แต่คอมโพเนนต์ Tabs มาตรฐานก็ครอบคลุมกรณีการใช้งานส่วนใหญ่ได้เป็นอย่างดี
import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons'
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#61DAFB',
tabBarInactiveTintColor: '#888',
tabBarStyle: {
backgroundColor: '#1a1a2e',
borderTopColor: '#2d2d44',
},
}}
>
<Tabs.Screen
name="home"
options={{
title: 'Home',
tabBarIcon: ({ color, size }) => (
<Ionicons name="home" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="search"
options={{
title: 'Search',
tabBarIcon: ({ color, size }) => (
<Ionicons name="search" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profile',
tabBarIcon: ({ color, size }) => (
<Ionicons name="person" size={size} color={color} />
),
}}
/>
</Tabs>
)
}ไฟล์หน้าจอของแต่ละ tab จะ export คอมโพเนนต์ React มาตรฐาน ไอคอน, ข้อความ และ badge ของ tab bar สามารถกำหนดค่าได้ผ่าน prop options ใน layout
Dynamic Routes และ Route Parameters
Segment แบบ dynamic ใช้วงเล็บเหลี่ยมในชื่อไฟล์ ไฟล์ที่ตั้งชื่อว่า [id].tsx จะจับคู่กับ segment เดียว ในขณะที่ [...slug].tsx จะจับคู่กับ segment ที่เหลือทั้งหมดในเส้นทาง
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 (
<View style={styles.container}>
<Stack.Screen options={{ title: `Product ${id}` }} />
<Text style={styles.heading}>Product Details</Text>
<Text style={styles.id}>ID: {id}</Text>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
heading: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 },
id: { fontSize: 16, color: '#888' },
})เมื่อเข้าถึง /product/42 หน้าจอนี้จะแสดงขึ้นพร้อมกับตัวแปร id ที่มีค่าเป็น "42" hook useLocalSearchParams ให้การเข้าถึง route parameters ทั้งหมดพร้อม type safety
สำหรับ catch-all routes นั้น [...slug].tsx จะจับคู่กับ path segments ทั้งหมด:
import { useLocalSearchParams } from 'expo-router'
export default function DocsScreen() {
// /docs/getting-started/installation → slug = ['getting-started', 'installation']
const { slug } = useLocalSearchParams<{ slug: string[] }>()
return <DocViewer path={slug.join('/')} />
}พร้อมที่จะพิชิตการสัมภาษณ์ React Native แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Typed Routes สำหรับความปลอดภัยในขั้นตอน Compilation
Expo Router สร้าง route types ขึ้นโดยอัตโนมัติเมื่อเปิดใช้ฟีเจอร์ typed routes การตรวจสอบประเภทนี้ช่วยตรวจจับลิงก์ที่เสียตั้งแต่ขั้นตอน compilation แทนที่จะปล่อยให้เกิดข้อผิดพลาดตอน runtime
เปิดใช้งานในไฟล์ app.json:
{
"expo": {
"experiments": {
"typedRoutes": true
}
}
}เมื่อเปิดใช้แล้ว prop href ของคอมโพเนนต์ Link และ argument ของ router.push() จะรับเฉพาะ string ที่ตรงกับ route ที่มีอยู่จริงเท่านั้น:
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')
}Typed routes ทำงานได้ดีเป็นพิเศษเมื่อใช้ร่วมกับ useLocalSearchParams เนื่องจาก types ที่สร้างขึ้นจะรับประกันว่าชื่อ parameter ในการกำหนด route ตรงกับชื่อที่ใช้ในคอมโพเนนต์ที่เรียกใช้ ช่วยป้องกัน bug ที่ยากต่อการตรวจจับซึ่งจะปรากฏก็ต่อเมื่อนำทางไปยังหน้าจอนั้นๆ เท่านั้น
Modal Screens และตัวเลือกการแสดงผล
Modal ใน Expo Router คือหน้าจอปกติที่กำหนดค่า presentation: 'modal' ไว้ใน layout แนวทางนี้ยังคงยึดตามหลักการของ file-based routing กล่าวคือ modal ก็เป็นเพียง route หนึ่งเช่นเดียวกับ route อื่นๆ
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
{/* Modal screen slides up from the bottom */}
<Stack.Screen
name="create-post"
options={{
presentation: 'modal',
headerTitle: 'New Post',
}}
/>
</Stack>
)
}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 (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Post title"
value={title}
onChangeText={setTitle}
/>
<Button title="Publish" onPress={handleSubmit} />
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
input: {
borderWidth: 1,
borderColor: '#333',
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 16,
},
})เมื่อเข้าถึง /create-post หน้าจอจะแสดงในรูปแบบ modal เลื่อนขึ้นจากด้านล่าง การเรียก router.back() จะปิด modal และนำผู้ใช้กลับไปยังหน้าจอก่อนหน้าใน navigation stack
การนำทางแบบ Programmatic และ Router API
นอกเหนือจากคอมโพเนนต์ Link แล้ว Expo Router ยังมี imperative API ผ่านออบเจ็กต์ router ซึ่งช่วยจัดการ navigation ที่ถูกเรียกจาก business logic แทนการกดปุ่มของผู้ใช้โดยตรง
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() ? (
<Button title="Back" onPress={() => router.back()} />
) : null
}ความแตกต่างระหว่าง router.push กับ router.replace มีความสำคัญอย่างยิ่งในกระบวนการ authentication หลังจากเข้าสู่ระบบสำเร็จ การใช้ router.replace('/dashboard') จะป้องกันไม่ให้ผู้ใช้กลับไปยังหน้าจอเข้าสู่ระบบได้ด้วยปุ่มย้อนกลับ
router.replace() จะแทนที่รายการปัจจุบันใน navigation history สำหรับ authentication guard ที่ต้อง redirect ผู้ใช้ที่ยังไม่ได้เข้าสู่ระบบ ควรใช้ <Redirect href="/login" /> ในส่วน render ของคอมโพเนนต์แทน เนื่องจากจะทำงานในขั้นตอน render และรองรับ server-side rendering บนเว็บได้อย่างถูกต้อง
Middleware และการป้องกันเส้นทาง
Expo Router v6 เปิดตัว server middleware สำหรับ logic ที่ต้องทำงานก่อนจะถึงคอมโพเนนต์ของ route ไฟล์ +middleware.ts จะดักจับ request ก่อนที่จะถึง route component ทำให้สามารถตรวจสอบ authentication, redirect และจัดการ HTTP headers ได้
import { type MiddlewareRequest } from 'expo-router/server'
export function middleware(request: MiddlewareRequest) {
const { pathname } = request.nextUrl
// Protect dashboard routes
const protectedPaths = ['/dashboard', '/settings', '/profile']
const isProtected = protectedPaths.some(p => pathname.startsWith(p))
if (isProtected) {
const token = request.cookies.get('session')
if (!token) {
return Response.redirect(new URL('/login', request.url))
}
}
return undefined // Continue to route
}บนแพลตฟอร์ม native นั้น server middleware จะไม่ทำงาน การป้องกัน route จึงต้องอาศัย guard ฝั่ง client แทน รูปแบบที่นิยมมากที่สุดคือการครอบ layout ที่ต้องการป้องกันด้วยการตรวจสอบ authentication:
import { Redirect, Stack } from 'expo-router'
import { useAuth } from '@/hooks/useAuth'
export default function AuthenticatedLayout() {
const { isLoggedIn, isLoading } = useAuth()
if (isLoading) return null
if (!isLoggedIn) return <Redirect href="/login" />
return <Stack />
}Server middleware ทำงานเฉพาะบนเว็บที่มีการใช้ server-side rendering เท่านั้น สำหรับแอปพลิเคชัน native จำเป็นต้องสร้าง guard ฝั่ง client ไว้ใน layout component การใช้ทั้งสองแนวทางร่วมกันจะรับประกันการป้องกันที่ครอบคลุมบนทุกแพลตฟอร์ม
สรุป
Expo Router v6 นำแนวทาง navigation ที่สอดคล้องกับมาตรฐานของเว็บเฟรมเวิร์กสมัยใหม่มาสู่ระบบนิเวศของ React Native สิ่งสำคัญที่ควรจดจำมีดังนี้:
- Expo Router v6 แทนที่การตั้งค่า navigation ด้วยตนเองด้วยหลักการ file-based convention โดยทุกไฟล์ในไดเรกทอรี
appจะกลายเป็น route โดยอัตโนมัติ - Layout ที่กำหนดผ่านไฟล์
_layout.tsxสร้างโครงสร้าง navigation แบบลำดับชั้น ไม่ว่าจะเป็น stack, tab หรือ drawer โดยไม่ต้องมีไฟล์ configuration แบบรวมศูนย์ - Dynamic routes ด้วย
[param].tsxและ catch-all routes ด้วย[...slug].tsxจัดการ navigation ที่มี parameter พร้อม TypeScript support อย่างเต็มรูปแบบ - Typed routes ตรวจจับลิงก์ navigation ที่เสียตั้งแต่ขั้นตอน compilation เมื่อเปิดใช้ฟีเจอร์นี้ใน
app.json - Modal screens, programmatic navigation ผ่าน
router.push/replace/backและ server middleware เป็นเครื่องมือครบชุดสำหรับระบบ routing - Route groups ที่ใช้ไดเรกทอรีล้อมด้วยวงเล็บช่วยจัดระเบียบโค้ดโดยไม่กระทบต่อ URL ทำให้โครงสร้างโปรเจกต์ยังคงอ่านง่ายแม้แอปพลิเคชันจะเติบโตขึ้น
- Guard ฝั่ง client ใน layout จัดการ authentication บน native ในขณะที่ server middleware ครอบคลุมฝั่งเว็บ การใช้ทั้งสองแนวทางร่วมกันให้การป้องกันที่ครบถ้วนบนทุกแพลตฟอร์ม
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
แท็ก
แชร์
บทความที่เกี่ยวข้อง

React Native: สร้างแอปมือถืออย่างสมบูรณ์ในปี 2026
คู่มือฉบับสมบูรณ์สำหรับการพัฒนาแอปมือถือ iOS และ Android ด้วย React Native ตั้งแต่การติดตั้งสภาพแวดล้อมจนถึงการเผยแพร่ ครบทุกพื้นฐานที่จำเป็นสำหรับการเริ่มต้น

Angular Standalone Components: คู่มือการย้ายระบบและแนวทางปฏิบัติที่ดีที่สุดในปี 2026
คู่มือการย้ายระบบ Angular standalone components อย่างละเอียด ขั้นตอนการลบ NgModules เปิดใช้งาน lazy loading และนำ standalone API มาใช้ใน Angular 21