expo / router

[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/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can I use /app inside /src

AndresdoSantos opened this issue · comments

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.

u can override EXPO_ROUTER_APP_ROOT like:

EXPO_ROUTER_APP_ROOT="../../src/app" expo start --clear

relative path from node_modules/expo-router/entry.js, default is ../../app

this didn't work for me

this did not work for me either - granted I'm using a monorepo.

The solution for me was to add this at the top of babel.config.js

process.env.EXPO_ROUTER_APP_ROOT = "../../src/app";

This really should be supported without this workaround

For people using this in monorepos, e.g. with turborepo, adding the following to app.config.js/ts works better.

process.env.EXPO_ROUTER_APP_ROOT = __dirname + "/src/app";

The solution for me was to add this at the top of babel.config.js

process.env.EXPO_ROUTER_APP_ROOT = "../../src/app";

In a new project also need to install react-native-dotenv and add config in babel.config.js in the plugins section

process.env.EXPO_ROUTER_APP_ROOT = '../../src/app';

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      ['module:react-native-dotenv'],
      require.resolve('expo-router/babel'),
      'nativewind/babel',
    ],
  };
};

Not working for me. It gives below output.
image

My babel config

module. Exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: [
      "@babel/plugin-proposal-export-namespace-from",
      [
        "module:react-native-dotenv",
        {
          envName: "APP_ENV",
          moduleName: "@env",
          path: ".env",
        },
      ],
      ["react-native-paper/babel"],
      [
        "module-resolver",
        {
          alias: {
            src: "./src",
          },
        },
      ],
      [require. Resolve("expo-router/babel")],
      [
        "react-native-reanimated/plugin",
        {
          relativeSourceLocation: true,
        },
      ],
    ],
  };
};

Not working for me. It gives below output. image

My babel config

module. Exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: [
      "@babel/plugin-proposal-export-namespace-from",
      [
        "module:react-native-dotenv",
        {
          envName: "APP_ENV",
          moduleName: "@env",
          path: ".env",
        },
      ],
      ["react-native-paper/babel"],
      [
        "module-resolver",
        {
          alias: {
            src: "./src",
          },
        },
      ],
      [require. Resolve("expo-router/babel")],
      [
        "react-native-reanimated/plugin",
        {
          relativeSourceLocation: true,
        },
      ],
    ],
  };
};

Hello @geetesh-gupta , you add the process.env.EXPO_ROUTER_APP_ROOT = '../../src/app'; line in the top of the babel.config.js file?

Sorry @sturmenta , forgot to copy that in the previous snippet. Yes, I defined the process.env variable at the top of the babel config.

Seems you must also modify the index/entry file:

import { registerRootComponent } from "expo";
import { ExpoRoot } from "expo-router";

// Must be exported or Fast Refresh won't update the context
export function App() {
  const ctx = require.context("./src/app");
  return <ExpoRoot context={ctx} />;
}

registerRootComponent(App);

That and setting process.env.EXPO_ROUTER_APP_ROOT = "../../src/app"; did the trick for me.

If it is not working, make sure to clear cache. npm start -c

Sorry @sturmenta , forgot to copy that in the previous snippet. Yes, I defined the process.env variable at the top of the babel config.

@geetesh-gupta were you able to solve the problem?

Hello @sturmenta, I'm sorry for the delay in updating you on the status of the issue. I was able to resolve the cache problem. While cleaning the cache did not work initially, I found success by closing Visual Studio code and reopening the project after clearing the cache. Everything seems to be working smoothly now.

Same here, I have to modify index.ts and copy/paste from expo-router/entry to make it work,

Seems you must also modify the index/entry file:

import { registerRootComponent } from "expo";
import { ExpoRoot } from "expo-router";

// Must be exported or Fast Refresh won't update the context
export function App() {
  const ctx = require.context("./src/app");
  return <ExpoRoot context={ctx} />;
}

registerRootComponent(App);

That and setting process.env.EXPO_ROUTER_APP_ROOT = "../../src/app"; did the trick for me.

Among the solutions proposed here, this was the one that worked for me!

commented

Would it be possible to add this to the documentation? I think a couple of people will encounter this and having it documented would be great!

Would it be possible to add this to the documentation? I think a couple of people will encounter this and having it documented would be great!

In my opinio expo haven't added this to docs because they want to create a better way to do it. It actually should have an easier way. Some config file or something. Should not be that hard.

In a TypeScript environment, I was trying to use @juliolmuller's solution. However, I couldn't make it work. The solution I found was to allow the index.js file inside my tsconfig and extracted the index/entry into my index.js.
tsconfig:
{ "compilerOptions": { "allowJs": true }, "extends": "expo/tsconfig.base", "include": ["index.js"] }

Can this be reopened? None of the solutions above have worked for me using Expo with TypeScript.

The option of just copying the contents of the entry file is not a good option, as even the Expo docs say:

Do not use this to change the root directory (app) as it won't account for usage in any other places.

Hey @skoshy . I created an expo-router template where I solved this issue. Feel free to use the template or check my solution how I solved this: https://github.com/ritmillio/expo-starter-kit

Hey @skoshy . I created an expo-router template where I solved this issue. Feel free to use the template or check my solution how I solved this: https://github.com/ritmillio/expo-starter-kit

What are the benefits of moving the default App folder to under a /src root folder? Or is it for a cleaner organization of files and components?

Hey @skoshy . I created an expo-router template where I solved this issue. Feel free to use the template or check my solution how I solved this: https://github.com/ritmillio/expo-starter-kit

What are the benefits of moving the default App folder to under a /src root folder? Or is it for a cleaner organization of files and components?

There is no benefit except a more organized file structure.

These were the steps to follow to succeed in moving the app folder to the src folder and using absolute path.

  1. Install react-native-dotenv by running the following command in your terminal:
# npm
npm install react-native-dotenv
# yarn
yarn add react-native-dotenv
  1. Move the index.ts file to the src folder. Update the "main" field in your package.json to point to the new location of index.ts. Modify your package.json as follows:
"main": "src/index.ts",
  1. Configure Babel to use react-native-dotenv and module-resolver. Open your babel.config.js file and make the following modifications:
process.env.EXPO_ROUTER_APP_ROOT = '../../src/app';

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      'module:react-native-dotenv',
      require.resolve('expo-router/babel'),
      [
        'module-resolver',
        {
          root: ['.'],
          alias: { '@': './src' },
          extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js'],
        },
      ],
    ],
  };
};
  1. Update your TypeScript configuration. Open your tsconfig.json file and add the baseUrl and paths configuration as shown below:
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
  1. Delete the .expo folder in your project directory. This folder may contain cached files that could conflict with the updated project structure.
  2. Rebuild your project by running the following command in your terminal:
# npm
npm run android -c
# yarn
yarn android -c

After following these steps, your project structure should resemble the following:

├── src
│   ├── app
│   │   ├── _layout.tsx
│   │   ├── [...missing].tsx
│   │   ├── modal.tsx
│   │   └── (tabs)
│   │       ├── index.tsx
│   │       ├── _layout.tsx
│   │       └── two.tsx
│   ├── assets
│   │   ├── fonts
│   │   │   └── SpaceMono-Regular.ttf
│   │   └── images
│   │       └── splash.png
│   ├── components
│   │   └── Themed.tsx
│   ├── constants
│   │   └── Colors.ts
│   ├── app.d.ts
│   └── index.ts
├── app.json
├── babel.config.js
├── package.json
├── tailwind.config.js
├── tree.txt
├── tsconfig.json
└── yarn.lock

My babel config:

process.env.EXPO_ROUTER_APP_ROOT = '../../src/router';

module.exports = function (api) {
  api.cache(true);
  return {
    presets: [['babel-preset-expo', {jsxRuntime: 'automatic'}]],
    plugins: [
      require.resolve('expo-router/babel'),
      [
        'transform-inline-environment-variables',
        {
          include: ['EXPO_ROUTER_APP_ROOT'],
        },
      ],
    ],
  };
};

Make sure to run yarn add --dev babel-plugin-transform-inline-environment-variables and additionally npx expo start --clear to clear the cache. However, I'm still not sure if this is enough just yet.

EDIT:

Support for src/app will be added in #629, but if you want something like 'src/router' there will be no official support.

Expo Router v2 supports src/app out of the box. Please don't use EXPO_ROUTER_APP_ROOT manually.

commented

Thank You @EvanBacon! I assume that the @ annotation would work for all code then within src?

We should update tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

https://docs.expo.dev/guides/typescript/#path-aliases

Expo Router v2 supports src/app out of the box. Please don't use EXPO_ROUTER_APP_ROOT manually.

The auto app or app/src folder is not taking effect on my project at all, Ive had to import my custom dir and pass it as the context prop down to the ExpoRoot component on the index.js.

Which solves any issues till I enable the typedRoutes flag, at that point it breaks:

[Error: ENOENT: no such file or directory, scandir 'C:\Users\...\repo\mobile\app']

repo is the git repository,
mobile is the project folder,
app is where it's looking for the route files.

When my routes are actually stored under repo/mobile/src/routes.

Any help would be appreciated.

For people using this in monorepos, e.g. with turborepo, adding the following to app.config.js/ts works better.

process.env.EXPO_ROUTER_APP_ROOT = __dirname + "/src/app";

@ItsWendell did you specify "main" file in the package.json?

Yes, sorry I forgot to mention it.
Adding the main prop to the package.json made no changes at all, it still looks at the index.js besides the package.json.
(if that file is deleted then it complains about no entrypoint, as I mentioned in #799)

These were the steps to follow to succeed in moving the app folder to the src folder and using absolute path.

  1. Install react-native-dotenv by running the following command in your terminal:
# npm
npm install react-native-dotenv
# yarn
yarn add react-native-dotenv
  1. Move the index.ts file to the src folder. Update the "main" field in your package.json to point to the new location of index.ts. Modify your package.json as follows:
"main": "src/index.ts",
  1. Configure Babel to use react-native-dotenv and module-resolver. Open your babel.config.js file and make the following modifications:
process.env.EXPO_ROUTER_APP_ROOT = '../../src/app';

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      'module:react-native-dotenv',
      require.resolve('expo-router/babel'),
      [
        'module-resolver',
        {
          root: ['.'],
          alias: { '@': './src' },
          extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js'],
        },
      ],
    ],
  };
};
  1. Update your TypeScript configuration. Open your tsconfig.json file and add the baseUrl and paths configuration as shown below:
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
  1. Delete the .expo folder in your project directory. This folder may contain cached files that could conflict with the updated project structure.
  2. Rebuild your project by running the following command in your terminal:
# npm
npm run android -c
# yarn
yarn android -c

After following these steps, your project structure should resemble the following:

├── src
│   ├── app
│   │   ├── _layout.tsx
│   │   ├── [...missing].tsx
│   │   ├── modal.tsx
│   │   └── (tabs)
│   │       ├── index.tsx
│   │       ├── _layout.tsx
│   │       └── two.tsx
│   ├── assets
│   │   ├── fonts
│   │   │   └── SpaceMono-Regular.ttf
│   │   └── images
│   │       └── splash.png
│   ├── components
│   │   └── Themed.tsx
│   ├── constants
│   │   └── Colors.ts
│   ├── app.d.ts
│   └── index.ts
├── app.json
├── babel.config.js
├── package.json
├── tailwind.config.js
├── tree.txt
├── tsconfig.json
└── yarn.lock

It worked to me. Now, July, 28 - 2023

Thanks a lot. I'm so glad for this

@orivaldogama thanks for the input!
I still have the same issue with the file or directory not being found.
I feel like we are using different commands, what does this do for you?

yarn android -c

Can you share your scripts? Mine are as following:


  "scripts": {
    "start": "expo start --dev-client",
    "clear": "expo start --dev-client --clear",
    "android": "react-native run-android"
  },

The "android" command runs fine, it's the start/clear that fails with that error.
Thanks

Perhaps this will help:

  1. Run yarn add --dev babel-plugin-transform-inline-environment-variables
  2. In your metro.config.js:
module.exports = function (api) {
  api.cache(true);
  return {
    presets: [['babel-preset-expo']],
    plugins: [
      [
        'transform-inline-environment-variables',
        {
          include: ['EXPO_ROUTER_APP_ROOT'],
        },
      ],
      require.resolve('expo-router/babel'),
      '@babel/plugin-transform-export-namespace-from',
      'react-native-reanimated/plugin',
    ],
  };
};
  1. Run npx expo start --clear (--clear/-c flag is important!)

@LunatiqueCoder I think you mean the babel config on step 2.
I still get the same exact error, not sure if the loading order for the plugins should be something specific for it to work? Also in some places I saw the require.resolve('expo-router/babel'), as you did, some just the expo-router/babel, I've tried some different variations with no luck.
Here is my current babel.config.js

process.env.EXPO_ROUTER_APP_ROOT = './src/app';

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo', 'module:metro-react-native-babel-preset'],
    plugins: [
      'expo-router/babel',
      'react-native-paper/babel',
      [
        'module-resolver',
        {
          root: ['.'],
          alias: {
            '@': './src',
          },
          extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js'],
        },
      ],

      [
        'transform-inline-environment-variables',
        {
          include: ['EXPO_ROUTER_APP_ROOT'],
        },
      ],
      require.resolve('expo-router/babel'),
      '@babel/plugin-transform-export-namespace-from',
      'react-native-reanimated/plugin',
    ],
  };
};

Thanks

@rzfzr Indeed, I updated my post accordingly.

Looking at your code, the first line seems to be the problem. Either delete it, or change it to process.env.EXPO_ROUTER_APP_ROOT = '../../src/app';

👇 Quote #41 (comment)

relative path from node_modules/expo-router/entry.js, default is ../../app

With or without process.env.EXPO_ROUTER_APP_ROOT = '../../src/app'; the result is exactly the same, it is as if it wasn't using that constant, as it always complains about not finding the file/dir on a path that never includes the src folder.
Error:

[Error: ENOENT: no such file or directory, scandir 'C:\Users\Rafael\Desktop\GitGud\sma\app\app'] {      
  errno: -4058,
  code: 'ENOENT',
  syscall: 'scandir',
  path: 'C:\\Users\\Rafael\\Desktop\\GitGud\\sma\\app\\app'
}

sma is the repo dir
the first app is the expo project subdir, (confusing I know, I already had this structure on multiple repos before adopting the expo-router....)
the second app is the default path which I'm not using, it does not exist, I'm trying to use sma/app/src/routes
This is the same error as my first post, just that there I renamed the folders.

@EvanBacon

Expo Router v2 supports src/app out of the box. Please don't use EXPO_ROUTER_APP_ROOT manually.

Even with expo-router v2 i still get this error when trying to do an EAS update:

[expo-cli] Using src/app as the root directory for Expo Router.
[expo-cli] Starting Metro Bundler
[expo-cli] iOS Bundling failed 1544ms
[expo-cli] SyntaxError: node_modules/expo-router/_ctx.ios.tsx: node_modules/expo-router/_ctx.ios.tsx:Invalid call at line 2: process.env.EXPO_ROUTER_APP_ROOT
[expo-cli] First argument of `require.context` should be a string denoting the directory to require.
[expo-cli] Error: node_modules/expo-router/_ctx.ios.tsx:Invalid call at line 2: process.env.EXPO_ROUTER_APP_ROOT

For anyone arriving here late you can override v2 of the router. You just define the EXPO_ROUTER_APP_ROOT_2. See https://github.com/expo/expo/blob/923016204c239cdad07b89626ac65eb505623d98/packages/expo-router/babel.js#L92

There are various use cases for this. In my case I prefer not to use the inline typescript compiler for a variety of reasons, hence I compile my files to a folder called dist-ts. Also worth noting that the path needs to be relative, not absolute.

babel.config.js example

process.env.EXPO_ROUTER_APP_ROOT_2 = "../../dist-ts";

module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['expo-router/babel'],
  };
};

@EvanBacon 👍 for supporting /src/app, but it would be great if ExpoRouter also let you customize the directory name. From this comment it looks like you may be already planning on it. :)

@kevin-ashton just tried to change that ROOT_2 constant, still no changes, expo still fails looking for the routes on the old path...
Any updates on this?

@AndresdoSantos May I ask why this issue was closed?

Expo Router only supports app/ and src/app. We have no immediate plans for these paths to be customised. We ask that people don't use the env vars mentioned, as the are mainly used for unit testing specific features and will not work in all scenarios. They may change unexpectedly and will not be covered by semver.

I am using turborepo and had to no-hoist expo-router on my app. On top of this, I had to patch each of the _ctx.*.js files to not use any of the env variables. I replaced them with the actual values and it worked... Don't like the solution but it works as of now for me

This issue should be re-opened since I'm experiencing the same error on Expo SDK 50 and Router v3.

@tn531 read the above reply from @marklawlor, this has been flagged as non issue/won't fix...

@rzfzr This was release in the v3, but its not a pattern we recommend. https://docs.expo.dev/router/reference/src-directory/#custom-directory

@tn531 As per the README.md, this repo is in maintenance mode and is only maintained for v2 security issues. If you have an issue we are more than happen to investigate, but you will need to create an issue on the correct repo https://github.com/expo/expo

I have solved this issue by creating a index.js file on root and adding this code which have ctx with src path

//index.js
import { registerRootComponent } from "expo";
import { ExpoRoot } from "expo-router";

// Must be exported or Fast Refresh won't update the context
export function App() {
  const ctx = require.context("./src/app"); //Path with src folder
  return <ExpoRoot context={ctx} />;
}

registerRootComponent(App);

and change the main in package.json like this

"main": "index.js",

I have solved this issue by creating a index.js file on root and adding this code which have ctx with src path

//index.js
import { registerRootComponent } from "expo";
import { ExpoRoot } from "expo-router";

// Must be exported or Fast Refresh won't update the context
export function App() {
  const ctx = require.context("./src/app"); //Path with src folder
  return <ExpoRoot context={ctx} />;
}

registerRootComponent(App);

and change the main in package.json like this

"main": "index.js",

This worked for me. Thanks.

Among both solutions what worked for me was putting process.env.EXPO_ROUTER_APP_ROOT = "../../src/app"; override

image