Search code examples
javascriptreactjswebpackcreate-react-appes6-modules

Conditionally import assets in create-react-app


Is it possible to conditionally import assets when creating a React app using create-react-app? I'm aware of the require syntax - example:

import React from "react";


const path = process.env.REACT_APP_TYPE === "app_1" ? "app_1" : "app_2";

const imagePath = require(`./assets/${path}/main.png`);

export default function Test() {
  return (
      <img src={imagePath} alt="" />
  );
}

This however bundles all my assets no matter what.

It will load the proper image, but it will still bundle all the files together in the final build.

When I look in the dev tools for the final build, I can see all the assets there even though I only wanted to load the assets for app_1.

Am I forced to touch the webpack config, if so, what should I change? or is there another way?


Solution

  • You will need to use webpack (or other bundler.) The code is not being run when it's bundled, so the compiler has no way of knowing which branch of logic to follow (app_1 or app_2). Therefore you have to get into the bundler's logic in order to achieve your goal.

    However, this isn't as scary as it seems since webpack has built in capability to do this (no 3rd parties required...)

    I would look into using webpack.providePlugin

    (https://webpack.js.org/plugins/provide-plugin)

    or its sibling DefinePlugin

    (https://webpack.js.org/plugins/define-plugin)

    (I'm afraid these examples are off the top of my head, so it's very unlikely they'll work on first pass.)

    Examples:

    Both will require a provider module...

    // in path/provider.js
    
    module.exports = {
      live: '/path/to/live/image',
      dev: '/path/to/dev/image'
    }
    

    Provide Plugin Example

    // in webpack
    
    new webpack.ProvidePlugin({
        imagePath: [
          'path/provider',         // the file defined above
          process.env.ENVIRONMENT  // either 'dev' or 'live'
        ]
      }),
    
    // in code
    
    export default function Test() {
      return (
          <img src={imagePath} alt="" />
      );
    }
    

    Define Plugin example:

    // in webpack
    
    new webpack.DefinePlugin({
      'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
    });
    
    // in code
    
    var providers = require('path/provider'); // same path provider as above
    
    export default function Test() {
      return (
          <img src={providers[process.env.ENVIRONMENT]} alt="" />
      );
    }
    
    

    In both cases the bundler is forced to collapse your variable to an actual literal value at compile time - before bundling has taken place. Since you have now collapsed the logical path down to a single option, it is now free to only bundle the relevant assets.