import { Stack } from 'expo-router';
export default function HomeLayout() {
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: '#1e293b' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' },
}}
>
{/* Ecranul principal din tab-ul Home */}
<Stack.Screen name="index" options={{ title: 'Feed Principal' }} />
{/* Ecranul de detalii deschis peste feed */}
<Stack.Screen name="[id]" options={{ title: 'Detalii Produs' }} />
</Stack>
);
}Am migrat recent o aplicație de mobil cu vreo 14.000 de utilizatori activi de la React Navigation clasic la Expo Router (v3). Trecerea n-a fost chiar o plimbare în parc, dar am scăpat de vreo 300 de linii de boilerplate doar din configurarea rutei de deep linking. Dacă te gândești să faci pasul sau te lovești acum de structura de directoare, hai să-ți zic ce-am învățat pe pielea mea.
Când merită migrarea și care e compromisul?
React Navigation e sfânt. Îl știm toți, îl folosim de ani de zile și funcționează. Totuși, când ai de gestionat zeci de ecrane, fișierul ăla imens de configurare devine un coșmar de mentenanță.
Expo Router vine cu abordarea file-based, similară cu Next.js. Sună bine pe hârtie, dar are un trade-off major. Este excelent dacă vrei deep linking nativ fără să-ți prinzi urechile în regex-uri și vrei suport bun de web. În schimb, dacă ai o logică extrem de custom de navigare (de exemplu, ecrane dinamice generate la runtime din backend), structura rigidă de fișiere o să te încurce teribil. Pentru 90% din aplicații însă, Expo Router este o alegere foarte bună.
Cum configurezi Tab + Stack nested?
Aici se blochează majoritatea lumii. În React Navigation clasic scriai componente imbricate în cod. În Expo Router totul se bazează pe foldere și grupuri rotunde (grup).
Să luăm cazul clasic: ai un ecran de tip Tabs (Home, Profile), iar pe tab-ul Home vrei să deschizi un ecran de detalii (Detail Screen) care să facă push pe stack.
Structura de fișiere trebuie să arate cam așa:
app/
├── (tabs)/
│ ├── _layout.tsx <-- Definește Tab-urile
│ ├── home/
│ │ ├── _layout.tsx <-- Stack-ul din interiorul tab-ului Home
│ │ ├── index.tsx <-- Ecranul principal de Home
│ │ └── [id].tsx <-- Ecranul de detalii (dynamic route)
│ └── profile.tsx <-- Al doilea tab
└── _layout.tsx <-- Root layout (e.g. Auth provider)
Configurația din cod și UX-ul
În layout-ul principal al tab-ului (app/(tabs)/_layout.tsx), definești tab-urile fizice. Nimic complicat. Magia se întâmplă în sub-folderul home. Acolo pui un alt _layout.tsx care este de tip <Stack>. Astfel, orice navigare în interiorul folderului home va face push pe stack-ul respectiv, păstrând contextul tab-ului activ.
Un detaliu important peste care am dat: dacă vrei ca ecranul de detalii să ascundă complet bara de taburi (să fie full screen), structura de mai sus nu te ajută deoarece stack-ul e înăuntrul tab-ului. Pentru a ascunde tab-urile pe ecranul de detalii, trebuie să pui stack-ul la nivel de root (app/_layout.tsx) și să ai tab-urile ca o singură rută în acel stack. Practic, muți [id].tsx în afara folderului (tabs). E o chestie de UX care mie mi-a luat ceva timp să o aliniez corect cu designerii noștri.
La nivel de performanță, nu am sesizat nicio diferență sesizabilă între cele două abordări. Expo Router folosește tot React Navigation sub capotă, deci e doar un strat de abstracție.
Voi ce folosiți pe proiectele noi de React Native? Ați trecut complet pe file-based routing sau preferați în continuare controlul manual din React Navigation?