The create-react-app imports restriction outside of src directory

I am using create-react-app. I am trying to call an image from my public folder from a file inside my src/components. I am receiving this error message.

./src/components/website_index.js Module not found: You attempted to import ../../public/images/logo/WC-BlackonWhite.jpg which falls outside of the project src/ directory. Relative imports outside of src/ are not supported. You can either move it inside src/, or add a symlink to it from project's node_modules/.

import logo from '../../public/images/logo_2016.png'; <img className="Header-logo" src={logo} alt="Logo" />

I have read many things saying you can do an import to the path but that is still not working for me. Any help would be greatly appreciated. I know there are many questions like this but they are all telling me to import logo or image so clearly I am missing something in the big picture.

Answers:

Answer

This is special restriction added by developers of create-react-app. It is implemented in ModuleScopePlugin to ensure files reside in src/. That plugin ensures that relative imports from app's source directory don't reach outside of it.

You can disable this feature but only after eject operation of create-react-app project.

Most features and its updates are hidden into the internals of create-react-app system. If you make eject you will no more have some features and its update. So if you are not ready to manage and configure application included to configure webpack and so on - do not do eject operation.

Play by the existing rules (move to src). But now you can know how to remove restriction: do eject and remove ModuleScopePlugin from webpack configuration file.


Since create-react-app v0.4.0 the NODE_PATH environment variable allows to specify a path for absolute import.

Absolute import allows to use import App from 'App' instead import App from './App' relatively to the value specified in variable NODE_PATH.

This feature is specifically usefull for monorepos or other configuration questions but not for importing images or anything else from public folder.

Content of public folder will be placed in build folder and availabe by relative url. Also everything imported will be processed by webpack and will be placed also in build folder.

If you import something from public folder probably that thing will be duplicated in the build folder and will be available by two different url (or with different ways to load), which ultimately worsen the package download size.

Importing from the src folder is prferable and have advantages. Everythings will be packed by webpack to the bundle with chunks optimal size and for best loading efficiency.

Answer

To offer a little bit more information to other's answers. You have two options regarding how to deliver the .png file to the user. The file structure should conform to the method you choose. The two options are:

  1. Use the module system (import x from y) provided with react-create-app and bundle it with your JS. Place the image inside the src folder.

  2. Serve it from the public folder and let Node serve the file. create-react-app also apparently comes with an environment variable e.g. <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;. This means you can reference it in your React app but still have it served through Node, with your browser asking for it separately in a normal GET request.

Source: create-react-app

Answer

The package react-app-rewired can be used to remove the plugin. This way you do not have to eject.

Follow the steps on the npm package page (install the package and flip the calls in the package.json file) and use a config-overrides.js file similar to this one:

const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');

module.exports = function override(config, env) {
    config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin));

    return config;
};

This will remove the ModuleScopePlugin from the used WebPack plugins, but leave the rest as it was and removes the necessity to eject.

Answer

If your images are in the public folder then you should use

"/images/logo_2016.png"

in your <img> src instead of importing

'../../public/images/logo_2016.png'; 

This will work

<img className="Header-logo" src="/images/logo_2016.png" alt="Logo" />
Answer

You need to move WC-BlackonWhite.jpg into your src directory. The public directory is for static files that's going to be linked directly in the HTML (such as the favicon), not stuff that you're going to import directly into your bundle.

Answer

This restriction makes sure all files or modules (exports) are inside src/ directory, the implementation is in ./node_modules/react-dev-utils/ModuleScopePlugin.js, in following lines of code.

// Resolve the issuer from our appSrc and make sure it's one of our files
// Maybe an indexOf === 0 would be better?
     const relative = path.relative(appSrc, request.context.issuer);
// If it's not in src/ or a subdirectory, not our request!
     if (relative.startsWith('../') || relative.startsWith('..\\')) {
        return callback();
      }

You can remove this restriction by

  1. either changing this piece of code (not recommended)
  2. or do eject then remove ModuleScopePlugin.js from the directory.
  3. or comment/remove const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); from ./node_modules/react-scripts/config/webpack.config.dev.js

PS: beware of the consequences of eject.

Answer

You don't need to eject, you can modify the react-scripts config with the rescripts library

This would work then:

module.exports = config => {
  const scopePluginIndex = config.resolve.plugins.findIndex(
    ({ constructor }) => constructor && constructor.name === "ModuleScopePlugin"
  );

  config.resolve.plugins.splice(scopePluginIndex, 1);

  return config;
};
Answer

There are a few answers that provide solutions with react-app-rewired, but customize-cra exposes a special removeModuleScopePlugin() API which is a bit more elegant. (It's the same solution, but abstracted away by the customize-cra package.)

npm i --save-dev react-app-rewired customize-cra

package.json

"scripts": {
    - "start": "react-scripts start"
    + "start": "react-app-rewired start",
    ...
},

config-overrides.js

const { removeModuleScopePlugin } = require('customize-cra')

module.exports = removeModuleScopePlugin()
Answer

If you only need to import a single file, such as README.md or package.json, then this can be explicitly added to ModuleScopePlugin()

config/paths.js

const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = {
  appPackageJson: resolveApp('package.json'),
  appReadmeMD:    resolveApp('README.md'),
};

config/webpack.config.dev.js + config/webpack.config.prod.js

module.exports = {
  resolve: {
    plugins: [
      // Prevents users from importing files from outside of src/ (or node_modules/).
      // This often causes confusion because we only process files within src/ with babel.
      // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
      // please link the files into your node_modules/ and let module-resolution kick in.
      // Make sure your source files are compiled, as they will not be processed in any way.
      new ModuleScopePlugin(paths.appSrc, [
          paths.appPackageJson,
          paths.appReadmeMD         // README.md lives outside of ./src/ so needs to be explicitly included in ModuleScopePlugin()
      ]),
    ]
  }
}
Answer

the best solution is to fork react-scripts, this is actually mentioned in the official documentation, see: Alternatives to Ejecting

Answer

I think Lukas Bach solution to use react-app-rewired in order to modify webpack config is a good way to go, however, I wouldn't exclude the whole ModuleScopePlugin but instead whitelist the specific file that can be imported outside of src:

config-overrides.js

const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");

module.exports = function override(config) {
  config.resolve.plugins.forEach(plugin => {
    if (plugin instanceof ModuleScopePlugin) {
      plugin.allowedFiles.add(path.resolve("./config.json"));
    }
  });

  return config;
};
Answer

install these two packages

npm i --save-dev react-app-rewired customize-cra

package.json

"scripts": {
    - "start": "react-scripts start"
    + "start": "react-app-rewired start"
},

config-overrides.js

const { removeModuleScopePlugin } = require('customize-cra');

module.exports = function override(config, env) {
    if (!config.plugins) {
        config.plugins = [];
    }
    removeModuleScopePlugin()(config);

    return config;
};

Answer

If you need multiple modifications, like when using ant design, you can combine multiple functions like this:

const {
  override,
  removeModuleScopePlugin,
  fixBabelImports,
} = require('customize-cra');

module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
  }),
  removeModuleScopePlugin(),
);
Answer

Adding to Bartek Maciejiczek's answer, this is how it looks with Craco:

const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const path = require("path");

module.exports = {
  webpack: {
    configure: webpackConfig => {
      webpackConfig.resolve.plugins.forEach(plugin => {
        if (plugin instanceof ModuleScopePlugin) {
          plugin.allowedFiles.add(path.resolve("./config.json"));
        }
      });
      return webpackConfig;
    }
  }
};

Answer

Remove it using Craco:

module.exports = {
  webpack: {
    configure: webpackConfig => {
      const scopePluginIndex = webpackConfig.resolve.plugins.findIndex(
        ({ constructor }) => constructor && constructor.name === 'ModuleScopePlugin'
      );

      webpackConfig.resolve.plugins.splice(scopePluginIndex, 1);
      return webpackConfig;
    }
  }
};
Answer

Image inside public folder

  use image inside html extension
  <img src="%PUBLIC_URL%/resumepic.png"/>

  use image inside  js extension
  <img src={process.env.PUBLIC_URL+"/resumepic.png"}/>
  • use image inside js Extension

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.