React native Typescript path alias unable to resolve module

18.7k views Asked by At

so basically, I created my React Native with Typescript using the commandline in RN homepage: npx react-native init MyApp --template react-native-template-typescript

After that, I ran the project and it was built successfully. However, when I added the path alias to import my file, it threw an error: Unable to resolve module #folder/file from ... could not be found within the project or in these directories: node_modules

I've already followed some tutorials and bug resolves on Google but I've met no luck.

Here is my .babelrc file (I tried to change the file from babel.config.js to .babelrc as some resolver said but it still didn't work)

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    [
      "module-resolver",
      {
        "root": ["./src"],
        "extensions": [
          ".js",
          ".jsx",
          ".ts",
          ".tsx",
          ".android.js",
          ".android.tsx",
          ".ios.js",
          ".ios.tsx"
        ],
        "alias": {
          "#src/*": [
            "./src/*"
          ],
          "#configs/*": [
            "./src/config/*"
          ],
          "#actions/*": [
            "./src/redux/actions/*"
          ],
          "#reducers/*": [
            "./src/redux/reducers/*"
          ],
          "#store/*": [
            "./src/redux/store/*"
          ]
        }
      }
    ]
  ]
}

tsconfig.json

{
  "compilerOptions": {
    /* Basic Options */
    "target": "esnext", 
    "module": "commonjs",
    "lib": [
      "ES2017",
      "DOM",
      "ES2015",
      "ES2015.Promise"
    ], 
    "allowJs": true, 
    "jsx": "react-native",
    "noEmit": true, 
    "incremental": true,
    "importHelpers": true,
    "isolatedModules": true,
    "strict": true,
    "moduleResolution": "node",
    "baseUrl": ".", 
    "paths": {
      "#src/*": [
        "src/*"
      ],
      "#configs/*": [
        "src/config/*"
      ],
      "#actions/*": [
        "src/redux/actions/*"
      ],
      "#reducers/*": [
        "src/redux/reducers/*"
      ],
      "#store/*": [
        "src/redux/store/*"
      ]
    }, 
    "types": ["jest"],
    "allowSyntheticDefaultImports": true, 
    "esModuleInterop": true, 
    "skipLibCheck": false, 
    "resolveJsonModule": true 
  },
  "exclude": [
    "node_modules",
    "babel.config.js",
    "metro.config.js",
    "jest.config.js"
  ]
}

My folders and files structure

├───assets
├───components
├───config
│       constants.ts
│
└───redux
    ├───actions
    │       index.ts
    │       stateActions.ts
    │
    ├───reducers
    │       index.ts
    │       stateReducer.ts
    │
    └───store
            index.ts

Really looking forward to receive you guys answers. Thank you so much

P/s: if you dont mind, please take a look at my repository: https://github.com/NotJackieTruong/rn_test_typescript

8

There are 8 answers

0
mirsahib On

In tsconfig.json


{
...
   "compilerOptions":{
        ...
       "baseUrl":"."
       "paths":{
            "@folder": "src/folder" <--assuming app logic is in src/ 
        }
   }
}

In babel.config.js

module.exports = {
...
  plugins: [
    ["module-resolver",{
      "alias":{
        "@folder": "./src/folder",
      }
    }]
  ],
};

Uninstall the app from emulator/device

Restart the metro server yarn start --reset-cache

Build the app yarn run android/ios

0
Ahmed Younes On

For those who still stuck with the issue with all solutions you have just make sure the following:-

npm install --save-dev babel-plugin-module-resolver

or

yarn add --dev babel-plugin-module-resolver

babel.config.js or .babelrc

make sure this is development not production in babel <<<<<<<<<<<<< VIP

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['react-native-reanimated/plugin'],
  env: {
    development: { // <<< make sure this is development not production
      plugins: [
        'react-native-paper/babel',
        [
          'module-resolver',
          {
            root: ['./src'],
            extensions: [
              '.js',
              '.jsx',
              '.ts',
              '.tsx',
              '.android.js',
              '.android.tsx',
              '.ios.js',
              '.ios.tsx',
            ],
            alias: {
              '@hooks': ['./src/hooks'],
              '@familyway': ['./src'],
              '@assets': ['./src/Assets'],
              '@components': ['./src/Components'],
              '@constants': ['./src/Constants'],
              '@helpers': ['./src/helpers'],
              '@onboarding': ['./src/onBoarding'],
              '@redux': ['./src/redux'],
              '@screens': ['./src/Screens'],
              '@lib': ['./src/lib'],
              '@containers': ['./src/containers'],
            },
          },
        ],
      ],
    },
  },
};

ok now with the tsconfig.json


{
  "compilerOptions": {
    "target": "ES2022",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "esnext.asynciterable",
      "esnext.intl",
      "esnext.symbol",
      "esnext.array"
    ],
    "allowSyntheticDefaultImports": true,
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "noImplicitAny": true,
    // "noImplicitReturns": true,
    // "noImplicitThis": true,
    "esModuleInterop": true,
    "alwaysStrict": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "module": "commonjs",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-native",
    "incremental": true,
    "noEmitOnError": true,
    "baseUrl": "./src",
    "paths": {
      "@hooks/*": ["hooks/*"],
      "@components/*": ["Components/*"],
      "@assets/*": ["Assets/*"],
      "@screens/*": ["Screens/*"],
      "@constants/*": ["Constants/*"],
      "@helpers/*": ["helpers/*"],
      "@onboarding/*": ["OnBoarding/*"],
      "@redux/*": ["redux/*"],
      "@containers/*": ["containers/*"],
      "@lib/*": ["lib/*"]
    }
  },
  "include": [
    "next-env.d.ts",
    "additional.d.ts",
    "src/**/*.ts",
    "src/**/*.js",
    "src/**/*.tsx",
    "./App.tsx",
    "./src/**/*",
    "src/**/*",
    "src/**/*.*"
  ],
  "exclude": ["node_modules"]
}

0
deathangel908 On

For expo v49+:

  1. module-resolver is not required, you can remove it from babel-config

  2. Add to app.json:

{
  "expo": {
    "experiments": {
      "tsconfigPaths": true
    }
  }
}
  1. Define paths as usual in tsconfig.json:
{
  "compilerOptions": {
    "paths": {
      "@/*": [
        "src/*"
      ]
    }
  },
  "extends": "expo/tsconfig.base"
}

0
shehan chanuka On

In my case I was not using expo. I initiated my project using npx react-native init <App name>--template react-native-template-typescript. I ran into issues once I added path aliases to my .babelrc and tsconfig.js. I tries several instructions but nothing worked. My successful attempt was.

  1. Deleted node_modules and yarn.lock
  2. Converted babel.config.js into .babelrc. (Cannot gaurantee if this had to do with the success).
  3. Uninstalled the app from the emulator
  4. Ran yarn to install all the dependencies.
  5. Ran yarn start --reset-cache to install and run the app while removing the metro-cache.
5
AndreyProgr On

To make this work just need add package.json file to each directory you want to access with one property name in it. For example:

├───assets
└───components
    └───package.json

package.json content:

{
  "name": "components"
}

And then you can import like this:

import SomeComponent from 'components/SomeComponent';

There is also a good article describing how this works.

7
jted95 On

add this to your tsconfig.json

"include": ["./src/**/*"]

then restart your TypeScript Server

Cmd+Shift P then choose TypeScript: Restart TS server

Edit: this is the tsconfig.json I'm using if that helps

{
  "compilerOptions": {
    "noImplicitAny": true,
    "noEmit": true,
    "allowSyntheticDefaultImports": true,
    "allowJs": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "jsx": "react-native",
    "lib": ["es6", "dom", "esnext"],
    "moduleResolution": "node",
    "baseUrl": ".",
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "paths": {
      "@app/*": ["./src/*"]
    }
  },
  "include": ["src/**/*", "./App.tsx"],
  "extends": "expo/tsconfig.base"
}

0
Yilmaz On

tsconfig.json

 "baseUrl": ".", 
 "paths": {
      
      // this is for src directory
      "@*": ["src/*"],
      "@configs/*": ["src/config/*"
      ],

babel.config.js

module.exports = function (api) {
    api.cache(true);
    return {
        presets: ["babel-preset-expo"],
        plugins: [
            [
                "module-resolver",
                {
                    alias: {
                        "@config": "./src/config",
                         ....
                        
                    }
                }
            ]
        ]
    };
};


        }
0
mehmetdemiray On

After long efforts and trials, I was able to make the path alias work with "@" as follows;

Note: My all codes in src directory.

babel.config.js

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    [
      'module-resolver',
      {
        root: ['./src'],
        alias: {
          '@': './src'
        }
      }
    ]
  ]
};

tsconfig.json

{
  "extends": "@tsconfig/react-native/tsconfig.json",
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@/*": ["*"]
    }
  },
  "include": ["src"]
}