expo / router Goto Github PK
View Code? Open in Web Editor NEW[ARCHIVE]: Expo Router has moved to expo/expo -- The File-based router for universal React Native apps
Home Page: https://docs.expo.dev/routing/introduction/
[ARCHIVE]: Expo Router has moved to expo/expo -- The File-based router for universal React Native apps
Home Page: https://docs.expo.dev/routing/introduction/
I'm getting the error: Unable to resolve "expo-router/entry" from "app/index.tsx"
.
I have looked at #29, and it's not related to that issue it looks like. It's something that happened as soon as I tried to go from yarn
to pnpm
.
After following the basic Stack example via the documentation guide, unless the route is named index
, the router will always result in an unmatched route initially at start up.
https://github.com/frankcalise/expo-router-tabs-demo/blob/main/app/(root).js
firstly, thanks for this package it's amazing!
for implementing something like determining whether a link is active, the docs have an example with const { pathname } = useHref();
however, for me if i navigate to /home
this gives something like /(root)/(app)/(tabs)/home/index
. is there an api for determining what the current path is based on how you would route there? like e.g. just /home
instead of the above string
cheers!
Modals like the ones found in the Instagram website are pretty common on the web. We should document how to easily create this transparent floating effect for web-only cases:
Demo app is not running. After yarn install
& yarn start
getting this error
Metro has encountered an error: While resolving module `expo-router/build/exports`, the Haste package `expo-router` was found. However the module `build/exports` could not be found within the package. Indeed, none of these files exist:
* `/Users/roro/Programming/react-native/expoRouterDemoApp/packages/expo-router/build/exports(.native|.ios.ts|.native.ts|.ts|.ios.tsx|.native.tsx|.tsx|.ios.js|.native.js|.js|.ios.jsx|.native.jsx|.jsx|.ios.json|.native.json|.json)`
* `/Users/roro/Programming/react-native/expoRouterDemoApp/packages/expo-router/build/exports/index(.native|.ios.ts|.native.ts|.ts|.ios.tsx|.native.tsx|.tsx|.ios.js|.native.js|.js|.ios.jsx|.native.jsx|.jsx|.ios.json|.native.json|.json)`: /Users/roro/Programming/react-native/expoRouterDemoApp/node_modules/metro-resolver/src/resolve.js (199:9)
197 | candidates,
198 | };
> 199 | throw new MissingFileInHastePackageError(opts);
| ^
200 | }
201 |
202 | class MissingFileInHastePackageError extends Error {
ABI46_0_0RCTFatal
__37-[ABI46_0_0RCTCxxBridge handleError:]_block_invoke
_dispatch_call_block_and_release
_dispatch_client_callout
_dispatch_main_queue_drain
_dispatch_main_queue_callback_4CF
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
__CFRunLoopRun
CFRunLoopRunSpecific
GSEventRunModal
-[UIApplication _run]
UIApplicationMain
main
start_sim
0x0
w
to open the browserTest Firebase Auth
on the frontpagebrowserLocalPersistence
is undefined
https://github.com/Albert-Gao/expo-router-web-try-out/tree/firebase-error
Hi Evan,
as I said on Twitter already, awesome work on the router! I've started implementing it today and have removed sooo much code. My app has gotten a 100% more declarative. One thing I struggle wrapping my head around is auth. And I think it's such an important part of almost every app that it's worth having a best practice in the docs. I'm happy to contribute a PR for that once I've figured it out for myself. Before Expo Router, I was basically doing the following:
<Stack.Navigator>
{isLoggedIn ? (
<Stack.Screen name="Authenticated" component={Authenticated} />
) : isLoggedOut ? (
<Stack.Screen name="SignIn" component={SignIn} />
) : (
<Stack.Screen name="Loading" component={Loading} />
)}
</Stack.Navigator>
This way, only the relevant screens (and dependents) were mounted based on the auth state. Now with file-based routing, the files will always be there. I'm sure you have thought about auth already, any best practice you have in mind?
When working with <Stack.Screen>
and useLink
, we need to modify the values of paths we're using between them to make them work. Given this file path: /folder/index.tsx
:
// this works
<Stack.Screen name='folder/index' />
// this does not
<Stack.Screen name='folder' />
const link = useLink()
// cannot be found
link.push('/folder/index')
// this works
link.push('/folder')
I would expect both values would work in both contexts.
Edit: sorry about closing/reopening, accidentally hit the button :)
/status/*
is squatted by RN CLI dev server. We should move this endpoint to _expo/status
or related path.app
status.tsx <- cannot be visited.
I am trying this on Windows 11.
After following the steps from the docs, I can't manage to use navigation.
I will include a short video.
I tried everything from Troubleshooting page
No issues on Mac OS (m1) and iOS simulator
It'd be great to add support for a type-safe routing. Something similar to nextjs-routes.
I noticed that useLinkingConfig()
is always being console.logged to the terminal when the demo app is started/reloaded, is this by design? If so, is there a way to turn off this behavior?
Logging seems to happen in ContextNavigationContainer.tsx
on line 45.
My question is simple.
I created a new app with the expo-router template.
After doing this I moved the /app folder into /src and fast-refresh did not update my folder.
When I reloaded the entire app it showed the touch app/index.js
screen.
I wanted to know if there is any way to use it inside the /src folder.
Hi!
I want to ask if it is a known issue or not.
I still see a white screen after the Splash screen, for a second before I can see my app mounted.
This is my file system routes
And here is my (root).tsx file
import React from 'react';
import {Layout, SplashScreen} from 'expo-router';
import {NativeBaseProvider} from 'native-base';
import {useLanguageService} from 'src/hooks';
import useCachedResources from 'src/hooks/useCachedResources';
import {useStore} from 'src/store';
import {custom_theme} from 'src/theme/custom-theme';
import Animated, {SlideInLeft} from 'react-native-reanimated';
export default function Root() {
//Hooks
const isLoadingComplete = useCachedResources();
const {changeLanguage} = useLanguageService();
//Store selectors
const stored_lang = useStore.useLang();
const hasHydrated = useStore.useHasHydrated();
const isSignedIn = useStore.useIsSignedIn();
React.useLayoutEffect(() => {
if (hasHydrated) changeLanguage(stored_lang);
}, [hasHydrated]);
return (
<React.Fragment>
{/* When all loading is setup, unmount the splash screen component. */}
{!isLoadingComplete && !hasHydrated && <SplashScreen />}
{isLoadingComplete && hasHydrated && (
<Animated.View entering={SlideInLeft} style={{flex:1}} >
<NativeBaseProvider theme={custom_theme}>
<Layout>
<Layout.Screen
name="(user)"
redirect={!isSignedIn}
options={{
headerShown: false,
}}
/>
<Layout.Screen
name="(auth)"
redirect={isSignedIn}
options={{
headerShown: false,
}}
/>
<Layout.Children />
</Layout>
</NativeBaseProvider>
</Animated.View>
)}
</React.Fragment>
);
}
And my useCachedResources hook
import {FontAwesome} from '@expo/vector-icons';
import * as Font from 'expo-font';
import {useEffect, useState} from 'react';
export default function useCachedResources() {
const [isLoadingComplete, setLoadingComplete] = useState(false);
// Load any resources or data that we need prior to rendering the app
const loadResourcesAndDataAsync = async () => {
try {
// Load fonts
await Font.loadAsync({
...FontAwesome.font,
'space-mono': require('../assets/fonts/SpaceMono-Regular.ttf'),
'Ubuntu-Bold': require('../assets/fonts/ubuntu-fonts/Ubuntu-Bold.ttf'),
'Ubuntu-BoldItalic': require('../assets/fonts/ubuntu-fonts/Ubuntu-BoldItalic.ttf'),
'Ubuntu-Italic': require('../assets/fonts/ubuntu-fonts/Ubuntu-Italic.ttf'),
'Ubuntu-Light': require('../assets/fonts/ubuntu-fonts/Ubuntu-Light.ttf'),
'Ubuntu-LightItalic': require('../assets/fonts/ubuntu-fonts/Ubuntu-LightItalic.ttf'),
'Ubuntu-Medium': require('../assets/fonts/ubuntu-fonts/Ubuntu-Medium.ttf'),
'Ubuntu-MediumItalic': require('../assets/fonts/ubuntu-fonts/Ubuntu-MediumItalic.ttf'),
'Ubuntu-Regular': require('../assets/fonts/ubuntu-fonts/Ubuntu-Regular.ttf'),
});
} catch (e) {
// We might want to provide this error information to an error-reporting service
console.warn(e);
} finally {
setLoadingComplete(true);
}
};
useEffect(() => {
loadResourcesAndDataAsync();
}, []);
return isLoadingComplete;
}
And you can see the actual app in this video
I am not 100% sure if this is an expo-router issue, sorry in advance
First, its great work! amazing tool. Modal from deep link opens like stack screen, like says in docs (https://expo.github.io/router/docs/guides/modals)
but after then:
If you try to use it on windows, the path-linking system goes crazy and links the files to \\{routename}
.
Here is an example:
linking {"config": {"screens": {".\\index": ".\\index", "[...404]": "*", "_sitemap": "_sitemap"}}, "getInitialURL": [Function getInitialURL], "getPathFromState": [Function getPathFromState], "getStateFromPath": [Function getStateFromPath], "prefixes": ["exp://192.168.0.14:19000/--/"], "subscribe": [Function addEventListener]}
Create a new expo app and install the expo-router, create a new route and you can see the issue.
Sorry for bringing up the complicated use cases on day one already ๐คฃ
I'm looking to access a slug of a dynamic route that's higher up in the hierarchy. So imagine a route like this:
myapp://folders/[folderId]/files/[fileId]
Now, in [fileId].js I'd like to access both $folderId and $fileId.
What I've already tried:
Given the route myapp://folders/[folderId]/files
, route.params.folderId will not be availabe in files.js. But I can do the following in [folderId].js
to make it availabe:
<Drawer.Screen
name="files"
initialParams={{ routes.params.folderId }}
/>
However, as soon as I have another dynamic segment (fileId), this will no longer work and the only thing I can access in [fileId].js
is the route.params.fileId, not route.params.folderId (although React Navigation should shallow merge the initialParams with the params provided in navigation).
Any ideas on this?
hello! in our app we have a tab navigator with 3 nested stacks with screens that can be deep linked to.
our home stack uses a layout route so instead of /home
and /home/profile
we use /
and /profile
to mirror navigation in our web app (we're using solito so routes have to be identical across native and web).
if I deep link to a route in one of the other stacks (community/questions/123
) then do router.back()
, i am taken to the /community
route as expected
however, if i deep link to a route in the home stack (/profile
) and do router.back()
I get
The action 'GO_BACK' was not handled by any navigator.
Is there any screen to go back to?
This is a development-only warning and won't be shown in production.
I've attached a minimal repro with setup that resembles ours as closely as possible
I have tried setting this behaviour up by doing home/profile
instead of (home)/profile
and it seems to work as expected - is there any way to achieve this with fragment routes so that doing router.back()
after navigating to /profile
takes you to /
?
thanks so much in advance
https://github.com/louisholley/expo-router-issue-repro
npx uri-scheme open "exp://192.168.0.15:19000/--/community/questions/123" --ios
and see that pressing back in the header worksnpx uri-scheme open "exp://192.168.0.15:19000/--/profile" --ios
and see the error mentioned aboveExpo CLI should support rendering multiple HTML files, not just index.html. Running npx expo export
for web should crawl the app
directory, and statically render each file into a unique html file that will be output in the dist
directory.
index.html
. To completely remove this we will need support for dynamic routes, which is out of scope for this feature.externals
support to Metro. This will enable us to render a bundle with Metro while keeping react
intact (required for hooks).Is there an exported type we can use to type the props?
In Next.js this is type like:
context.params?.id // undefined | string | string[]
app/blog/[id].js
export default function BlogPost({ route }) {
return (
<Text>
{route.params.id}
</Text>
);
}
{Constants.manifest} is {}
on web platform, so there is for example no way to get the current version number via {Constants.manifest.version}
npx create-react-native-app -t with-router
Input expoconstants
when asked for the app name.
cd expoconstants
yarn expo install expo-constants
yarn web
touch app/index.js
Then in App.js
add
import { Text } from 'react-native'
import Constants from 'expo-constants'
export default function Home() {
return (
<Text>{JSON.stringify(Constants.manifest)}</Text>
)
}
This may be the wrong place to post this, but I'm not entirely sure where this issue belongs. I encountered the issue when trying to implement expo-router in one of my projects.
While I was converting over to metro from webpack in order to enable the use of expo-router, I ran into issues running the application on web. Images would always error with the message: Uncaught Error: Image: asset with ID "108" could not be found. Please check the image source or packager.
After digging in and debugging with a co-worker, it seems like this error only happens on Windows (Mac OS runs fine locally).
I also checked the Source tab in the browser and added some breakpoints to the bundle to check how assets were being loaded/used. To my surprise, it looked like the registerAsset
method in the bundle wasn't even running (causing the getAsssetById
method to throw an error since our asset array was empty).
This was the relevant block of code on Windows that is supposed to handle asset registration/retrieval:
__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAssetByID = getAssetByID;
exports.registerAsset = registerAsset;
var assets = [];
function registerAsset(asset) {
return assets.push(asset);
}
function getAssetByID(assetId) {
return assets[assetId - 1];
}
},212,[],"..\\..\\node_modules\\react-native-web\\dist\\modules\\AssetRegistry\\index.js");
After adding breakpoints, it was clear that the registerAsset
method was not being invoked and the following block ended up throwing errors since the asset came back null:
function resolveAssetUri(source) {
var uri = null;
if (typeof source === 'number') {
var asset = (0, _AssetRegistry.getAssetByID)(source);
if (asset == null) {
throw new Error("Image: asset with ID \"" + source + "\" could not be found. Please check the image source or packager.");
}
var scale = asset.scales[0];
if (asset.scales.length > 1) {
var preferredScale = _PixelRatio.default.get();
scale = asset.scales.reduce(function (prev, curr) {
return Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale) ? curr : prev;
});
}
var scaleSuffix = scale !== 1 ? "@" + scale + "x" : '';
uri = asset ? asset.httpServerLocation + "/" + asset.name + scaleSuffix + "." + asset.type : '';
} else if (typeof source === 'string') {
uri = source;
} else if (source && typeof source.uri === 'string') {
uri = source.uri;
}
if (uri) {
var match = uri.match(svgDataUriPattern);
if (match) {
var prefix = match[1],
svg = match[2];
var encodedSvg = encodeURIComponent(svg);
return "" + prefix + encodedSvg;
}
}
return uri;
}
Digging EVEN DEEPER, it looks like the registerAsset
method that was being used by the bundler was from a completely separate module react-native/Libraries/Image/AssetRegistry
=> ../../node_modules/@react-native/assets/registry.js
(I added breakpoints in that module and do see it running):
__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
module.exports = _$$_REQUIRE(_dependencyMap[0], "react-native/Libraries/Image/AssetRegistry").registerAsset({
"__packager_asset": true,
"httpServerLocation": "/assets/../../node_modules/expo-router/assets",
"width": 48,
"height": 48,
"scales": [1],
"hash": "b2de8e638d92e0f719fa92fa4085e02a",
"name": "forward",
"type": "png",
"fileHashes": ["b2de8e638d92e0f719fa92fa4085e02a"]
});
},580,[379],"..\\..\\node_modules\\expo-router\\assets\\forward.png");
So, my next question was - why is MacOS working? I had my coworker pull his bundle and noticed that the registerAsset block referenced a different module there. His referenced the same module that I saw was actually running on Windows ../../node_modules/@react-native/assets/registry.js
:
module.exports = _$$_REQUIRE(_dependencyMap[0], "@react-native/assets/registry");
},201,[202],"../../node_modules/react-native/Libraries/Image/AssetRegistry.js");
__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
'use strict';
var assets = [];
function registerAsset(asset) {
return assets.push(asset);
}
function getAssetByID(assetId) {
return assets[assetId - 1];
}
module.exports = {
registerAsset: registerAsset,
getAssetByID: getAssetByID
};
},202,[],"../../node_modules/@react-native/assets/registry.js");
So my assumption is that the module reference at the end of that block serves to override the module's handler so that it runs correctly. On Windows, it is trying to use ..\\..\\node_modules\\react-native-web\\dist\\modules\\AssetRegistry\\index.js
, which just seems wrong. MacOS is overriding the correct module ../../node_modules/@react-native/assets/registry.js
and thus doesn't have the issue.
https://github.com/kylegwalsh/expo-router-bundle-error-example
We need a configuration for rewriting incoming paths to new destinations. This will require the following:
expo.config.js
?). This could potentially be worked into app.config.js
too ๐คexpo-router
. The rewrites will need to be saved as application-side logic on native.getStateFromPath
.This feature should be considered blocking for the stable release.
ENG-6594
the generated folder is from relay compiler, which always create the codegen close to the route which uses GraphQL query.
the way next.js handles this is to add a property for the ignored folder name:
https://nextjs.org/docs/advanced-features/compiler#relay
Can we have something like this here?
I think, maybe we can make it an array, so we can have multiple paths being ignored rather than just ignoring the codegen folder.
possibility modify change tabs/stack options from screen are beautiful. like
import { Tabs } from "expo-router";
import { View } from "react-native";
export default function Page() {
return (
<View>
<Tabs.Screen options={{ title: "Home" }} />
...
but its not working, if app starts from deep link. for example, when i start demo app from exp://127.0.0.1/--/settings
i got:
the solution for now is to set the options for the tabs in the parents, like
<Tabs>
<Tabs.Screen name="settings" options={{ title: "Settings" }} />
<Tabs.Screen name="index" options={{ title: "Home" }} />
</Tabs>
Hey! I didn't see a troubleshooting item for this, so figured I'd create an issue as others may have run into this as well.
My new expo-router app, created via npx create-react-native-app -t with-router
, immediately crashes due to _$$_REQUIRE.context is not a function.
Troubleshooting steps followed
EXPO_ROUTER_APP_ROOT
is defined โ you can even see its value (the argument to require.context
) in the error message in my screenshot.The steps in troubleshooting don't seem to help โ you can see in the package.json
contents below that expo
's version is ^46.0.16
(I tried pinning 46.0.13
as well, which didn't help), and there is no metro.config.js
in the app root.
This app lives in a monorepo, but I don't think that's affecting this, since dependency versions are managed via syncpack
to prevent mismatches, and there are indeed no mismatches in the monorepo.
Any ideas would be greatly appreciated!
package.json
{
"name": "@genesis/app-contrib-react-native",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "expo start --dev-client",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web"
},
"dependencies": {
"expo": "^46.0.16",
"expo-dev-client": "~1.3.1",
"expo-router": "~0.0.28",
"expo-splash-screen": "~0.16.2",
"expo-status-bar": "~1.4.0",
"react": "18.0.0",
"react-dom": "18.0.0",
"react-native": "0.69.6",
"react-native-gesture-handler": "~2.5.0",
"react-native-reanimated": "^2.10.0",
"react-native-safe-area-context": "4.3.1",
"react-native-screens": "~3.15.0",
"react-native-web": "^0.18.9"
},
"devDependencies": {
"@babel/core": "^7.18.6"
},
"resolutions": {
"metro": "^0.73.1",
"metro-resolver": "^0.73.1"
},
"overrides": {
"metro": "^0.73.1",
"metro-resolver": "^0.73.1"
}
}
Getting the following error while bundling for iOS simulator. Using pnpm.
Unable to resolve module expo-router/entry from <root>/apps/expo/index.js: expo-router/entry could not be found within the project or in these directories:
node_modules
../../node_modules
node_modules
../../node_modules
> 1 | import "expo-router/entry"
| ^
2 |
package.json:
"dependencies": {
"app": "workspace:*",
"expo": "46.0.15",
"expo-router": "^0.0.25",
"expo-splash-screen": "~0.16.1",
"expo-status-bar": "~1.4.0",
"react": "18.0.0",
"react-dom": "18.0.0",
"react-native": "0.69.6",
"react-native-gesture-handler": "~2.5.0",
"react-native-reanimated": "~2.9.1",
"react-native-safe-area-context": "4.3.1",
"react-native-screens": "~3.15.0",
"react-native-web": "~0.18.7"
},
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/runtime": "^7.18.6",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
"typescript": "^4.6.3"
}
I was trying to navigate to the _sitemap of my project, but when I try to scroll down, it does not work. (See video)
On ios, scrolling is perfectly fine.
hey, i'm not sure if this is an issue or whether it's something wrong with my setup but this is my file setup
app/
(home).tsx
(home)/
index.tsx
inbox/
index.tsx
[id].tsx
(home).tsx is just a stack like this
import { Stack } from "expo-router";
export default Stack;
(home)/index.tsx renders a custom header with <Stack.Screen ...
which is working fine
however trying to render the same custom header with Stack.Screen
within inbox/index or inbox/[id] doesn't work - it renders the default react navigation header. even if i add a layout root (home)/inbox.tsx with another stack like <Stack screenOptions={{headerShown:false}} />
, the default header is rendered for the inbox screens
any ideas? no clue how to proceed!
I don't see a special step for this in the docs (other than resolving metro's version). yarn why metro
resolves 0.72.3
which is correct, so maybe this additional step should be documented?
For context, this is my original metro file:
// Learn more https://docs.expo.io/guides/customizing-metro
/**
* @type {import('expo/metro-config')}
*/
const { getDefaultConfig } = require('expo/metro-config')
const path = require('path')
const projectRoot = __dirname
const workspaceRoot = path.resolve(__dirname, '../..')
const config = getDefaultConfig(projectRoot)
config.watchFolders = [workspaceRoot]
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(workspaceRoot, 'node_modules'),
]
module.exports = config
Maybe this would have worked had I not specified a custom config?
Hi,
I found that adding this
import { StacksProvider } from "@mobily/stacks";
would cause this error:
The reproduce repo is here: https://github.com/Albert-Gao/expo-router-web-try-out
remove the usage of <StacksProvider/>
would solve this problem.
As shown in the screenshot, @mobily/stacks
is not in the call stack, would be better if we can include this @mobily/stacks
in the call stack so we can identify this problem easier.
React Navigation currently does not support URL hashes on web, therefore expo-router
cannot either.
Hi! I was playing around with this library and found that Passing parameters to the routes is out of date with the latest expo-router dependency. The Link
API from the library had a different definition in the app where href
property expects the following in the docs;
<Link
href={{
screen: "details",
/* 1. Navigate to the details route with query params */
params: { itemId: 86, otherParam: "anything you want here" },
}}
>
But the Href
type actually expects below as mentioned in docs file;
<Link
href={{
pathname: "details",
// /* 1. Navigate to the details route with query params */
query: { itemId: 86, otherParam: "anything you want here" },
}}
>
package.json
{
"name": "expo-router-playground",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"expo": "~46.0.16",
"expo-router": "^0.0.29",
"expo-status-bar": "~1.4.0",
"react": "18.0.0",
"react-native": "0.69.6",
"react-native-safe-area-context": "4.3.1",
"react-native-screens": "~3.15.0"
},
"devDependencies": {
"@babel/core": "^7.12.9"
},
"overrides": {
"metro": "0.73.1"
},
"private": true
}
When following the authentication example in the docs I am facing a redirect issue.
I have the following file structure:
The code for restricting routes:
function RootLayout() {
// Use some global auth context to control the route access.
// const auth = AuthContext.useToken();
const auth = true;
return (
// Create a basic custom layout to render some children routes.
<Layout screenOptions={{ showHeader: false }}>
<Layout.Screen
name="auth"
// When the auth is available (user is signed in), restrict access to the sign-in page.
redirect={auth}
/>
<Layout.Screen
name="(app)/home"
// When the auth is unavailable (no user signed in), restrict access to all the routes in the `(app)` directory.
redirect={!auth}
/>
<Layout.Children />
</Layout>
);
}
When I toggle the auth constant to false it correctly redirects me to auth/index
but when I toggle the auth constant to true it redirects me to (auth)/index
instead of (auth)/home/index
.
Is this an issue with the router or I messed up something?
Thanks
All details are in the summary
How do you set the tab order in the demo app example?
The linking configuration of React Navigation currently does not support aliases for a route. This means we cannot support /
and /index
both going to routes named index.
We could potentially support this directly in expo-router
since we needed to write a custom getStateFromPath
and getPathFromState
.
This is currently blocking the stable release.
Sorry for using @next
version, couldn't help myself. I'm getting this behavior there:
I have app/_layout
and app/index.tsx
.
index
gets rendered as duplicate tabs, and [...404]
seems to get added there on its own. The second index
tab is opening the sitemap.
That's actually pretty useful, but I'm curious what this behavior is meant to be.
The docs have an example for protecting all routes in the (app)
folder where a code comment reads:
// When the auth is unavailable (no user signed in), restrict access to all the routes in the
(app)
directory.
During my testing the redirect
prop will only restrict access to the index page in the app directory here so a sub route within (app)
will remain active if they click a button to log out on that page.
You can see in the following video that when i click "Sign out" i am not redirected to the sign-up page.
I'm wondering if the cited comment from the docs is incorrect here and whether the expectation is that we would manually add a <Stack.Screen />
entry for every route? Or perhaps it should be doing what the comment suggests and there is a bug in the router?
Either way, I'd be super grateful of some guidance here on the recommended way to protect routes ๐
There are two types of bundle splitting in Metro:
This is not blocking the v1 release.
From /tabs/schedule/index
, unable to Link to details
or /details
via href string or href {{ screen: "details" }}
.
| - app
| -- (root)
| --- tabs
| ---- schedule.js (stack w/ Index and Details)
| ---- schedule
| ----- index.js
| ----- details.js
If I link directly to /tabs/schedule/details
it screen will be pushed on the stack properly
Example here: https://github.com/frankcalise/expo-router-tabs-demo/blob/main/app/(root)/tabs/schedule/index.js
Apps like Spotify, Twitter, Instagram, etc. all have the ability to present the same page from multiple tabs. This is achievable in React Navigation by either abandoning deep linking support, or by adding the same screen with different URLs. Neither of these would work with Expo Router.
Using the same route in different places is prevented as this would create a conflict:
app
(tabs).js
(tabs)
(home)
home.js # /home
post.js # /post
(search)
search.js # /search
post.js <- Conflict
We need something that works more like a stack, where the user manually defines the tabs:
app
(tabs).js # New layout that manually indicates `/home` and `/search` are initial routes
(tabs)
home.js
search.js
post.js # Since this isn't an initial route, any of the tabs can push it.
The new router would have multiple state arrays, one for every initial route.
{
routes: ['/home', '/search', '/post'],
state [
{ initial: '/home' stack: [] },
{ initial: '/search' stack: [] }
]
}
<Link />
operations would push a new screen on the stack, unless the link points to an initial route, in which case it will redirect to the initial route and pop the stack.push
).Ideally, we'd drop the automatic tabs and drawers in favor of this new system where you manually choose which routes are tabs, this is much closer to how web-only frameworks work today.
Loading assets from a 'base' route (e.g. /somepath
) works correctly, loading them from /assets/[path to assets]
.
When attempting to load assets such as fonts from a nested path (e.g. /nested/123
), the page attempts to load them from /nested/assets/[path to assets]
which is incorrect and leads to the page not rendering correctly. This only happens if you visit the page URL directly, or refresh the browser while on that page.
This happens with locally-sourced assets (e.g. those hosted inside the assets
folder) but also with node_modules (e.g. the expo-google-fonts
packages exhibit the same behaviour).
This test was done on the web platform - I haven't explicitly tested on mobile/native as there is no way to refresh the page to trigger the bug.
For clarification, I'm unsure if this is a router-specific issue or a bundler issue. Please move to the appropriate repo if this is not the correct one.
https://github.com/ChronSyn/expo-router_bug-report-assetpath
Unable to resolve module @react-navigation/core from /Users/fernandorojo/Developer/madison-hacks/zeeg/node_modules/expo-router/build/link/useLinkToPath.js: @react-navigation/core could not be found within the project or in these directories:
../../node_modules/expo-router/node_modules
../../node_modules
../../../node_modules
../../../../../node_modules
../../node_modules
> 1 | import { getActionFromState, getStateFromPath, NavigationContainerRefContext, } from "@react-navigation/core";
| ^
2 | import { LinkingContext } from "@react-navigation/native";
3 | import * as React from "react";
4 | import { Linking } from "react-native";
Honestly surprised to see this error.
This is what I see in my node_modules
^. @react-navigation/core
is a dependency of /native
, but it seems like it isn't getting resolved. Maybe because it's too many levels deep?
It is in a monorepo, so maybe this has something to do with it. Not sure. I don't have any react navigation packages installed manually anywhere. This is my package.json
:
{
"name": "zeeg-example",
"description": "Example app for zeego",
"version": "0.6.0",
"main": "index.js",
"scripts": {
"android": "react-native run-android",
"ios": "expo run:ios",
},
"dependencies": {
"@radix-ui/react-context-menu": "^0.1.6",
"@react-native-menu/menu": "^0.5.2",
"add": "^2.0.6",
"expo": "^46.0.0",
"expo-router": "^0.0.25",
"react": "18.0.0",
"react-dom": "16.13.1",
"react-native": "0.69.6",
"react-native-gesture-handler": "~2.5.0",
"react-native-ios-context-menu": "^1.14.0",
"react-native-reanimated": "~2.9.1",
"react-native-safe-area-context": "4.3.1",
"react-native-screens": "~3.15.0",
"react-native-web": "^0.17.5"
},
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/runtime": "^7.9.6",
"babel-loader": "^8.2.3",
"babel-plugin-module-resolver": "^4.0.0",
"babel-preset-expo": "~9.2.0",
}
}
import {
useBottomTabBarHeight,
} from '@react-navigation/bottom-tabs'
Try calling useBottomTabBarHeight
in a screen and it'll break, rather than giving you a number.
Error: Couldn't find the bottom tab bar height. Are you inside a screen in Bottom Tab Navigator?
Let me know if this is needed.
Hello!
Love this so much! Just tried following the docs exactly and got this when running "expo start"
error: Error: While resolving module `expo-router/entry`, the Haste package `expo-router` was found. However the module `entry` could not be found within the package. Indeed, none of these files exist:
any ideas?
Hello,
First of all, thank you for your work and excited to see the future of it!
I am facing an issue when using expo router with header buttons.
With that configuration, I get an error from react navigation : The 'navigation' object hasn't been initialized yet.
I supposed I get this from the usage of useLink or useHref. But I need this in these components.
So I tried with a Splashscreen in order to wait, but it does not change anything!
So is that an issue from expo router or in this particular case (in screenOptions) I have to use the navigation and route props (that are not homogenous) ?
Thanks!
Using expo 47.
// _layout.tsx
import { Stack } from 'expo-router';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { Provider } from 'react-redux';
import ProfileAndBackHeaderButton from '../src/components/ProfileAndBackHeaderButton';
import SettingsHeaderButton from '../src/components/SettingsHeaderButton';
import { store } from '../src/redux/store';
function RootLayout() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Provider store={store}>
<Stack
screenOptions={({ route }) => {
const isIndexRoute = route.name === 'index' || route.name === '/';
const title = isIndexRoute ? null : route.name;
return ({
title,
headerTransparent: isIndexRoute,
headerTitleAlign: 'left',
animation: 'fade',
headerLeft: () => (
<ProfileAndBackHeaderButton />
),
headerRight: () => (
<SettingsHeaderButton />
),
});
}}
/>
</Provider>
</GestureHandlerRootView>
);
}
export default RootLayout;
// ProfileAndBackHeaderButton
import { useHref, useLink } from 'expo-router';
import { Button, ButtonSize, Colors } from 'react-native-ui-lib';
import { AntDesign, Ionicons } from '@expo/vector-icons';
function ProfileAndBackHeaderButton() {
const href = useHref();
const link = useLink();
const onPressAction = () => {
if (href.href === '/') {
link.push('/profile');
} else {
link.back();
}
};
return (
<Button
style={{ width: 40, height: 40, marginRight: 10 }}
round
backgroundColor={Colors.white}
size={ButtonSize.small}
onPress={onPressAction}
>
{href.href === '/'
? <AntDesign name="user" size={24} color="black" />
: <Ionicons name="chevron-back-outline" size={24} color="black" />}
</Button>
);
}
export default ProfileAndBackHeaderButton;
// index.tsx
import { SplashScreen } from 'expo-router';
import { useEffect, useState } from 'react';
import { View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Text } from 'react-native-ui-lib';
import Map from '../src/components/Map';
function Home() {
const [isReady, setReady] = useState(false);
useEffect(() => {
// Perform some sort of async data or asset fetching.
setTimeout(() => {
setReady(true);
}, 100);
}, []);
return (
<>
{!isReady && <SplashScreen />}
<View style={{ flex: 1 }}>
<Map />
<SafeAreaView>
<Text>Oui</Text>
</SafeAreaView>
</View>
</>
);
}
export default Home;
I have the following structure:
(root).tsx
โ NativeStack(root)
index.tsx
users
[id].tsx
If I use NativeStack.Screen
inside of (root)/index.tsx
to set options
such as the title
, it works fine. However, if I do the same inside of (root)/users/[id].tsx
, the screen options don't apply.
If I apply the screen options inside of the (root).tsx
file as a child of NativeStack
, it does in fact work.
Reproduction here. The line that doesn't work properly is here.
I noticed that options are only being set in an effect, so I'm not sure why it doesn't work.
It's probably worth documenting that people should memoize options
or lift it out of render, since it's a dependency of a hook which calls setOptions
under the hood.
When a stack
layout is nested inside a parent tabs
layout, the nested stack screen is showing up under tabs list rather than starting it's own navigation pattern.
app/
tabs/
stack/ // stack 2
_layout.js
index.js
tab1.js
tab2.js
_layout.js // stack 1
index.js
In above file system, stack 2 is showing up under tabs screen rather than starting its own navigation context.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.