I use expo-router and there is a very big delay on android when navigating between pages (on ios there is very little delay, on web everything happens instantly)
I also tried to insert console.log in the components I go to, but it is executed instantly, and then you have to wait a few seconds for the page to display, also removed images from pages but it did not help.
Upd:
I changed <Slot/>
to <Stack/>
and it works good on all devices, why can slot influence on slowing down the redirects?
package.json
{
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
"start": "expo start --go",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"update": "eas update --branch preview --message \"Updating the app\"",
"build": "eas build -p android --profile preview"
},
"dependencies": {
"@expo/vector-icons": "^14.0.0",
"effector": "^23.2.0",
"effector-react": "^23.2.0",
"expo": "~50.0.6",
"expo-dev-client": "~3.3.8",
"expo-device": "~5.9.3",
"expo-font": "~11.10.2",
"expo-linking": "~6.2.2",
"expo-router": "~3.4.7",
"expo-splash-screen": "~0.26.4",
"expo-status-bar": "~1.11.1",
"expo-system-ui": "~2.9.3",
"expo-task-manager": "~11.7.2",
"expo-updates": "~0.24.10",
"expo-web-browser": "~12.8.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.73.4",
"react-native-gesture-handler": "~2.14.0",
"react-native-reanimated": "~3.6.2",
"react-native-reanimated-carousel": "^3.5.1",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-svg": "14.1.0",
"react-native-web": "~0.19.6"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/react": "~18.2.45",
"@typescript-eslint/eslint-plugin": "^7.0.1",
"@typescript-eslint/parser": "^7.0.1",
"eslint": "^8.56.0",
"eslint-config-universe": "^12.0.0",
"prettier": "^3.2.5",
"react-native-svg-transformer": "^1.3.0",
"react-test-renderer": "18.2.0",
"typescript": "^5.1.3"
},
"private": true
}
app.json
{
"expo": {
"name": "zlno-rn",
"slug": "zlno-rn",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./src/assets/images/icon.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./src/assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.feip.zlno"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./src/assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.feip.zlno"
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./src/assets/images/favicon.png"
},
"plugins": [
"expo-router",
[
"expo-updates",
{
"username": "grnv"
}
]
],
"extra": {
"router": {
"origin": false
},
"eas": {
"projectId": "179559a6-40d3-4039-9684-a5b67cfe25f9"
}
},
"runtimeVersion": "1.0.0",
"updates": {
"url": "https://u.expo.dev/179559a6-40d3-4039-9684-a5b67cfe25f9",
"fallbackToCacheTimeout": 60000
},
"owner": "grnv2"
}
}
index.tsx
import { useRootNavigationState, Redirect } from 'expo-router';
export default function App() {
const rootNavigationState = useRootNavigationState();
if (!rootNavigationState?.key) return null;
return <Redirect href="/home" />;
}
_layout.tsx
import FontAwesome from '@expo/vector-icons/FontAwesome';
import Home from '@screens/Home';
import { useFonts } from 'expo-font';
import { Slot } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export {
// Catch any errors thrown by the Layout component.
ErrorBoundary,
} from 'expo-router';
export const unstable_settings = {
// Ensure that reloading on `/modal` keeps a back button present.
initialRouteName: '(tabs)',
};
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [loaded, error] = useFonts({
SpaceMono: require('@assets/fonts/SpaceMono-Regular.ttf'),
RobotoBlack: require('@assets/fonts/Roboto-Black.ttf'),
RobotoBlackItalic: require('@assets/fonts/Roboto-BlackItalic.ttf'),
RobotoBold: require('@assets/fonts/Roboto-Bold.ttf'),
RobotoBoldItalic: require('@assets/fonts/Roboto-BoldItalic.ttf'),
RobotoItalic: require('@assets/fonts/Roboto-Italic.ttf'),
RobotoLight: require('@assets/fonts/Roboto-Light.ttf'),
RobotoLightItalic: require('@assets/fonts/Roboto-LightItalic.ttf'),
RobotoMedium: require('@assets/fonts/Roboto-Medium.ttf'),
RobotoMediumItalic: require('@assets/fonts/Roboto-MediumItalic.ttf'),
RobotoRegular: require('@assets/fonts/Roboto-Regular.ttf'),
RobotoThin: require('@assets/fonts/Roboto-Thin.ttf'),
RobotoThinItalic: require('@assets/fonts/Roboto-ThinItalic.ttf'),
MontserratRegular: require('@assets/fonts/Montserrat-Regular.ttf'),
MontserratLight: require('@assets/fonts/Montserrat-Light.ttf'),
...FontAwesome.font,
});
// Expo Router uses Error Boundaries to catch errors in the navigation tree.
useEffect(() => {
if (error) throw error;
}, [error]);
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Slot />
</GestureHandlerRootView>
);
}
if you are navigating to the next screen that is too heavy and has more thing to render on the initial render. you can optimize the screen by lazy loading.
load only those components that necessary and lazy load the other. you can also use a hook useIsReady
import {useNavigation} from '@react-navigation/native'; import React from 'react';
you can use this like this