create-react-app: Can't import image from src

So I have a list of reviews each with an logo field which is a path to the image. The images are in an images folder in my src folder.

Example logo: 'src/images/logos/zoosk-logo.png',

I have a Reviews component which renders out Review components from the list passing the review object as a prop.

In the Review component I then display the logo in an image tag. Like so:

<img className="review__logo" src={this.props.review.logo} alt=""/>

This works great with npm start, but when I use the run build, the images aren’t copied over to the build file so the links don’t work. I know if you import the image then webpack will bundle it in the build step. But I can’t import it, because the path comes from a prop, and imports can only be done at the top.

Is there anyway to go around this? I’m relatively new to react.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 7
  • Comments: 35 (21 by maintainers)

Most upvoted comments

No, you don’t need to eject for this. Just import those images wherever you create this object.

import logo from './logo.png'
import logo2 from './logo2.png'

export default {
  logo,
  logo2
}

Then pass this object down as you like. Importing images can be done in any file.

Your use case is actually fully supported thanks to an awesome webpack feature called dynamic require

Instead of manually writing all your import statement, you can dynamically require your images with runtime parameters like require('./teams/' + team + '/' + image + '.svg') and webpack will auto-magically figure out what files could match this expression and include them in your bundle.

Here is an example based on the create-react-app starter template:

image

If you run npm run build on this app, you get a bundle that will look like this:

image

Webpack automatically figured-out all the files that could match

require(`./teams/${team}/${image}.svg`)

and included them in your bundle 😃

To clarify. Your mental model shouldn’t be “I need to import files right where they will be used” (like in Inage components). Your mental model should be “There should never be string literals in my code meaning absolute URLs to assets because build system won’t know to include them. So every time I want to write a string literal for image URL, I should instead write an import. The result of that import will be the string I want (and can pass anywhere else).”

This is now supported in 0.5.0.

Read about using the new public folder.

See also migration instructions and breaking changes in 0.5.0.

I’m a little unclear on this…I followed the README to issue #28 about possibly adding a static assets folder, and then found this issue. After reading the above comments, i’m still unclear on what I’m supposed to do in my use case. Let me explain:

I have folders of logos for sports teams, organized by league. This results in MANY logos, i.e.:

/mlb
  arizonaDiamondbacks.svg
  atlantaBraves.svg
  ...*.svg
/nba
  atlantaHawks.svg
  bostonCeltics.svg 
  ...*.svg
/nfl
  arizonaCardinals.svg
  atlantaFalcons.svg
  ...*.svg
/*

The list goes on and on. I have a .json file where each team has an id and a league name that maps to the file’s name. So when I map over each team’s object, the URL to the team’s image is dynamically inserted as a background-image in CSS, i.e.:

<h3 style={{backgroundImage: `url(assets/img/${team.league}/${team.id}.svg)`}}>
  {team.name}
</h3>

If I switch over to using react-create-app (which I’d like to, because configuring webpack and keeping dependencies up to date is a headache for me) I’m unsure of how I would handle this case. Using import is impossible (unless you can dynamically import somehow??). If I understand this comment it seems like I’m supposed to create a separate JS file where I have to manually import and export every-single-file that I’m going to use, i.e.

import atlantaHawks from 'nba/atlantaHawks';
import atlantaBraves from 'mlb/atlantaBraves';
...
export {
  atlantaHawks,
  atlantaBraves
  ...
}

And then I import that in my file where I map over each object and just dynamically map somehow? that to imported file’s name, i.e.:

import {*} from 'logos';

// later in render()
// somehow my `id` matches the import name? i.e. import {atlantaHawks} matches `team.id` when it's 'atlantaHawks' ??
<h3 style={{backgroundImage: [team.id]}}>
  {team.name}
</h3>

That seems like a lot of work to have to manually keep a list of files i’m importing/exporting? Seems like it’d be a lot easier to just keep track of my files and I can reference them myself in a static folder as I see fit. Could somebody shed some light on my use case here? I’m unclear how I should handle this when i’m not just referencing one or two images.

We’ll likely support this in some way in 1.0. We just need to look at more use cases and decide on the optimal way to do that.

For your use case, just copying files seems reasonable and I agree we should allow it in the future. In this case they would be handled outside of the build system, without imports.

But the downside is that your images won’t get “revved” so if some of them change, you’ll have to manually add ?version or something like this to the query string, or browsers will cache older versions. In addition, if a file is accidentally missing or misspelled, you won’t know this at compile time. All of this works automatically if you use imports.

As I said, I’m open to supporting your use case. I’m just struggling to figure out how to enable it in a way that people understand that importing is still what you should do in 90% of cases. I’m worried that if we just add support for “static” folder that gets copied as is, people will always put images there by habit, and won’t realize that there are benefits to doing it via imports (compile errors on missing file, cache busting, potentially image minification if we enable it).

If you have a proposal on how to enable something like a static folder, but somehow make it clear it is an escape hatch for projects with hundreds of assets unrelated to components, rather than the normal way of adding images, I’d love to hear it.

I am keeping this issue open because I want to address it, but I’m not sure how yet.

@gaearon @iRoachie worked like a charm for me too. Much like @iRoachie, I’m also coming from an Angular background, where I didn’t use even npm or webpack. Other day I was trying to do that with file-loader… Webpack feels confusing for newcomers (at least for me).

Maybe a small section about importing static files would add a lot of value to create-react-app, as it was created to ease the configuration process - even being related to Webpack, not React.

Yep per docs PUBLIC_URL is set only in production. In development it is set to empty string as expected, and if you reference <img src={process.env.PUBLIC_URL + ‘/img/logo.png’} /> the dev server essentially serves /img/logo.svg, when the file is in your public folder under /public/img/logo.png

As this is an old issue, I would appreciate it if you open a new one if you have additional questions on this.

I think there is a section on “adding images” in usage guide. Is there a way to make it more helpful?

@gaearon why isn’t webpack require recommended to use ? I don’t understand why this is not a standard feature of webpack as it’s in its documentation

I would love to use this feature myself as I have a structured folder of assets and manually importing them seems too heavy to maintain and using the public folder implies possible cache issues or 404 for the users.

Sorry if my question looks dumb; I just started using React and this project is a very smooth way to learn it !

I drafted a proposal to solve this in https://github.com/facebookincubator/create-react-app/pull/703. Unless we find some fatal flaws, it should come out in 0.5.0. Let me know what you think!

This feature was discussed in #28, but the previous conclusion was that it is out of scope for Create React App. The recommended way to handle image assets is using imports, because it also helps us ensure that when the image changes, the browser cache is busted correctly. This is easy to miss, when images are only copied over and not fully handled by the build system.

Would copying the files to the build folder or deploying them to a CDN with a separate script work for your use case?

For you ES6 fans out there here is the image fetcher:

import kris01 from './kris01.jpg';
import kris02 from './kris02.jpg';

let imgs = {
    kris01,
    kris02
};

let getImage = (key) => imgs[key];

export default getImage;

@bugzpodder yeah that’s a problem. This whole assets thing is really overcomplicated. There needs to be an asset exposer function in the service worker. @bugzpodder I don’t have any questions on this. I’m just a bit annoyed at the current work-around. Here’s what I ended up doing. This works perfectly but I am not looking forward to maintaining a static list of images:

import kris01 from './kris01.jpg';
import kris02 from './kris02.jpg';

let imgs = {
    kris01,
    kris02
};

function getImage(key) {
    return imgs[key];
}

export default getImage;

Here’s my wonderful little component that will be used in many places throughout my web app:

import React from 'react';
import getImage from './imgMap';

class ImageWithCaption extends React.Component {

    constructor(props) {
        super(props);
        this.image =
            {
                captionText: this.props.captionText || 'Default',
                uri: this.props.uri || '#',
                alt: this.props.alt || 'Alternative'
            };
    }

    render() {
        const IMAGE = getImage(this.image.uri);
        return (
            <span className="col-double">
                <a href={this.image.uri}>
                    <img src={IMAGE} alt={this.image.alt}></img>
                </a>
                <p className="caption ng-binding">{this.image.captionText}</p>
            </span>
        );
    }
}

export default ImageWithCaption;

So anyone else who is stuck on this for hours-on-end will hopefully have somewhat of a solution, or rather a hack, for development mode.

Totally understand where you’re coming from. As I’m relatively unfamiliar with using import for things like images, I was just unsure if my particular use case should be bent to fit the import model used here. Plus, the README pointed me to this issue saying “if you prefer static assets … please let us know”. So perhaps my case is one you can consider in future implementations.

As far as how to make that clear, I’m not sure. But something along the lines of what you already have would go a long way I think, i.e. the docs specifically say “if you need to use images, use import” with an explanation/example. Then as a footnote to that it could say “oh and if you need static images or assets, create a folder name static and anything in there will copied directly over, but beware for X, Y, Z reasons”. Having to create a static folder that doesn’t exist by default might possibly help deter people from thinking “oh, i can just drop everything in here”, i.e. if they create the folder they presumably read the docs and warnings (however I grant that it could become possible that they just read on a stack overflow answer “hey just create a folder named static and everything in there gets copied” so they don’t get that context from the docs on why they should use import in 90% of cases).

I was looking for this right now. Maybe create-react-app could have a default img or assets folder to move it’s content into the build file?