Explain Codes LogoExplain Codes Logo

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

javascript
import-restrictions
create-react-app
webpack-config
Alex KataevbyAlex Kataev·Mar 8, 2025
TLDR

Easily overcome the create-react-app import limitation by creating a symlink within src that points to an external directory:

ln -s ../path-to-external-directory src/external

Then, import the module like it's in the src directory:

import something from 'external/myModule'; // "Look, Ma! No ejecting!"

This method keeps your imports clean and compliant with create-react-app without having to tweak any configurations. However, bear in mind that symlinks may not be interoperable with all build tools or CI/CD pipelines.

Digging Deeper: Understanding the Why

But why does create-react-app enforce such a rule? It largely boils down to keeping the project structure clean and builds predictable. If imports could originate from anywhere, it'd likely hinder dependency management and lead to chaotic builds.

Sticking to best practices

Before turning to tweaking configurations or ejecting, consider these recommended practices:

Utilizing Environment Variables

Use process.env.PUBLIC_URL when dealing with assets:

<img src={process.env.PUBLIC_URL + '/img/logo.png'} /> // All roads lead to Rome... err... src?

Following the "src" Way

Consider placing reusable modules and assets within the src directory, as this allows seamless module bundling:

import logo from './images/logo.png'; // "Who's got two thumbs and loves 'src'? This dev!"

Dealing with ModuleScopePlugin

create-react-app uses ModuleScopePlugin to restrict file imports within src. But after proper risk analysis, you can consider disabling it. Tools like react-app-rewired or Craco can get the job done.

// with react-app-rewired config.resolve.plugins = config.resolve.plugins.filter( (plugin) => !(plugin instanceof ModuleScopePlugin) );

Keep in mind, this opens Pandora's Box, exposing your app's source directory outside its protective home.

Use react-app-alias to alias directories

// jsconfig.json or tsconfig.json { "compilerOptions": { "baseUrl": "src", "paths": { "assets/*": ["../assets/*"] // You shall not pass... oh, wait. } } }

Use rewire or react-app-rewired for modifying your app internals

These tools enable you to alter the webpack configuration and gain more control without the need for ejecting.

Veering off the Beaten Path: Understanding the Risks

Ejecting from create-react-app

Ejecting gives full-on customization. But it's a one-way street and needs expertise in build tooling:

npm run eject // "I'm going on an adventure!" - Bilbo Baggins

Modifying webpack with advanced tooling

react-app-rewired and Craco provide the capability to tweak webpack config with less trauma than risk-prone ejecting:

// craco.config.js module.exports = { webpack: { configure: (webpackConfig) => { webpackConfig.resolve.plugins.forEach((plugin, i) => { if (plugin.constructor.name === 'ModuleScopePlugin') { delete webpackConfig.resolve.plugins[i]; // "You have no power here!" } }); return webpackConfig; }, }, };

Evaluating the Trade-offs

Adjustments lead to increased flexibility, but potentially influence upgradability. Every create-react-app update may risk breaking your custom setup. Remember the key rule: "Benefits versus maintenance liability."

To Learn More: Documentation and Community

Scour the references below and engage in community discussions to further understand nuances and best practices.