Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't import image from src #585

Closed
iRoachie opened this issue Sep 5, 2016 · 35 comments
Closed

Can't import image from src #585

iRoachie opened this issue Sep 5, 2016 · 35 comments
Milestone

Comments

@iRoachie
Copy link
Contributor

iRoachie commented Sep 5, 2016

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.

@schuchowsky
Copy link

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?

@fson
Copy link
Contributor

fson commented Sep 5, 2016

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?

@iRoachie
Copy link
Contributor Author

iRoachie commented Sep 5, 2016

@fson Oh I see, i thought it could've been done without ejecting. Copying the files to the build folder would be the better approach for me. Any material you could point me to that would allow me to do this?

@gaearon
Copy link
Contributor

gaearon commented Sep 5, 2016

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.

@gaearon
Copy link
Contributor

gaearon commented Sep 5, 2016

This works great with npm start

Since 0.4.0, it shouldn't. (As you noted it was broken in production in the first place.) Please use imports in JS instead, or please explain why JS imports don't satisfy your use case. Thanks!

@gaearon gaearon added this to the 1.0.0 milestone Sep 5, 2016
@gaearon
Copy link
Contributor

gaearon commented Sep 5, 2016

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)."

@iRoachie
Copy link
Contributor Author

iRoachie commented Sep 5, 2016

Thanks a lot @gaearon i'm actually coming from an angular background so i'm new to react. Thanks for guiding me on the right path. Solution works !

@gaearon
Copy link
Contributor

gaearon commented Sep 5, 2016

This is not really React-specific by the way. It's just that we think automatic bundling of assets with content hashes and compile-time checks for missing files is valuable enough that we ask people to use this mechanism for importing instead. The feature is more related to Webpack, not to React.

Is there something in Usage Guide we could change to make this use case clearer?

@iRoachie
Copy link
Contributor Author

iRoachie commented Sep 5, 2016

Oh i guess that's true. The docs are great man, just a new way of thinking for me since i'm used to referencing static assets the more traditional way. Thanks again

@schuchowsky
Copy link

@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.

@gaearon
Copy link
Contributor

gaearon commented Sep 5, 2016

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

@schuchowsky
Copy link

Yes, the section is there. I think I was so focused on Webpack loaders that couldn't find it. It's very clear, can't be more helpful than that. Thanks.

@iRoachie
Copy link
Contributor Author

iRoachie commented Sep 5, 2016

Well I guess you could make a note saying that it's the only way of referencing local files.

@gaearon
Copy link
Contributor

gaearon commented Sep 5, 2016

@iRoachie Would you like to send a PR to make it more helpful?

@jimniels
Copy link

jimniels commented Sep 5, 2016

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.

@gaearon
Copy link
Contributor

gaearon commented Sep 6, 2016

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.

@gaelollivier
Copy link
Contributor

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 :)

@iRoachie
Copy link
Contributor Author

iRoachie commented Sep 6, 2016

@gaelduplessix When I tried that it gave the error that *img is not a module

@gaearon
Copy link
Contributor

gaearon commented Sep 6, 2016

We don't officially support Webpack's "non-standard" features like require.context. They happen to work but we make no promises about continuing to support them, and may break them any time.

Ideally we'd like to forbid their usage just to make this clear, but we haven't had time to work on this.

@gaelollivier
Copy link
Contributor

@gaearon Indeed, just stumbled on this comment saying it's not officially supported... That's still a workaround until there's a supported way for this use case though.

@jimniels
Copy link

jimniels commented Sep 6, 2016

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).

@gaearon
Copy link
Contributor

gaearon commented Sep 7, 2016

Yea, this totally makes sense. Thanks for the conversation. I'll cut a release when I'm back from vacation in about two weeks.

@gaearon gaearon modified the milestones: 0.5.0, 1.0.0 Sep 22, 2016
@gaearon
Copy link
Contributor

gaearon commented Sep 22, 2016

I drafted a proposal to solve this in #703. Unless we find some fatal flaws, it should come out in 0.5.0. Let me know what you think!

@gaearon
Copy link
Contributor

gaearon commented Sep 22, 2016

Closing as this is fixed, and will be released in 0.5.0.

@gaearon gaearon closed this as completed Sep 22, 2016
@gaearon
Copy link
Contributor

gaearon commented Sep 23, 2016

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.

@3dos
Copy link

3dos commented May 17, 2017

@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 !

@anshumanv
Copy link

@gaearon is it possible to set favicon of the app from src directory and deleting the one present in public directory?

@Timer
Copy link
Contributor

Timer commented Dec 8, 2017

No @anshumanv

@GalGreenfield
Copy link

For my case using imports is problematic, as I have an object class that one of its properties is an image URL, and I want to provide a path, but React doesn't recognize that path in this way.

@JSONRice
Copy link

JSONRice commented Jun 4, 2018

@gaearon good insights. As of the patch is seems that PUBLIC_URL only works in production mode. The documentation is very confusing. Here is what I have as the docs have noted:

render() {
  // Note: this is an escape hatch and should be used sparingly!
  // Normally we recommend using `import` for getting asset URLs
  // as described in “Adding Images and Fonts” above this section.
  return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
}

Turns out my process.env.PUBLIC_URL is always empty and is so even after attempting:

PUBLIC_URL="https://somepublicurl.com/" npm start

I can set this variable in my .bashrc but what I don't understand is why this is empty. From the docs it appears to be that way when dev mode is ran. Shouldn't any images in public automatically be served up statically anyways? I'll try your flat file approach and see if that works.

@bugzpodder
Copy link

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.

@JSONRice
Copy link

JSONRice commented Jun 4, 2018

@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.

@JSONRice
Copy link

JSONRice commented Jun 4, 2018

Last but not least I'm invoking the component with this for now (demo):

            <ImageWithCaption
                id="kris01"
                captionText="Hi my name is Kris"
                uri="kris01"
                alt="Kris 01"
            ></ImageWithCaption>

@JSONRice
Copy link

JSONRice commented Jun 4, 2018

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;

@skptricks
Copy link

Get Image from Local Resource Folder in React Native
React native provides a unified media management system so developers can easily manage all the image files by placing them together into a single folder. So here is the complete step by step tutorial for Show Image from Local Resource Folder in react native.

https://www.skptricks.com/2018/07/get-image-from-local-resource-folder-in-react-native.html

@judypatoodie
Copy link

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.

I am unable to import images that are outside of the src file.

@lock lock bot locked and limited conversation to collaborators Jan 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests