Create-react-app: public directory css url

Created on 28 Oct 2020  ยท  17Comments  ยท  Source: facebook/create-react-app

Describe the bug

When trying to import a image using the url method in a css/sass file the path no longer resolves to include the public directory using the prefix /

Did you try recovering your dependencies?

Yes

Which terms did you search for in User Guide?

Environment

  current version of create-react-app: 4.0.0
  running from /Users/jfay/.npm/_npx/12069/lib/node_modules/create-react-app

  System:
    OS: macOS 10.15.7
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  Binaries:
    Node: 12.18.3 - /usr/local/bin/node
    Yarn: 1.21.1 - /usr/local/bin/yarn
    npm: 6.14.6 - /usr/local/bin/npm
  Browsers:
    Chrome: 86.0.4240.75
    Edge: Not Found
    Firefox: 80.0
    Safari: 14.0
  npmPackages:
    react: ^17.0.1 => 17.0.1 
    react-dom: ^17.0.1 => 17.0.1 
    react-scripts: ^4.0.0 => 4.0.0 
  npmGlobalPackages:
    create-react-app: Not Found

Steps to reproduce

  1. Start with a generic cra w/ [email protected]
  2. Open App.css
  3. Set the background to use an image resolved to the public url
.App {
  text-align: center;
  background-image: url("/logo192.png");
}

Expected behavior

Prior to upgrading to react-scripts@latest version 3.4.3 resolved images referenced in css files to include the public directory.

Actual behavior

Screen Shot 2020-10-28 at 12 33 07 PM

bug report needs triage

Most helpful comment

I was about to make report the same issue. Though I'm trying to update from 3.4.3 to 4.0

All 17 comments

I was about to make report the same issue. Though I'm trying to update from 3.4.3 to 4.0

Same here with scss module.
I'm trying to upgrade from 3.4.3 to 4.0

I think I found a related commit by @atlanteh here:
https://github.com/facebook/create-react-app/commit/fa648daca1dedd97aec4fa3bae8752c4dcf37e6f

  • Support scss absolute path resolution for url()

Adding resolve-url-loader broke all apps using scss with centralized assets folder and all url(./assets/.png) broke (#7023).
This change allows apps to use url(/assets/
.png) and it would map to src/assets/*.png

Does this means that now / refers to the src folder instead of public ? So the size limit to inline image in data url would become irrelevant ๐Ÿค” (because, correct me if I'm wrong but I don't think we should serve src)

And what about fonts and other references of file in css ? ๐Ÿค”

@thomasleduc No.
My commit fixes an issue I had when using cra with sass after upgrading to cra 3.4.x (don't remember now which version) which caused the exact issue you are describing. The commit causes the loader to correctly load the files during build time from the src folder and copy them to the Public folder.
This is very weird. Can you try reverting my commit locally on your machine and see if that's the culprit?
If this is in fact the one, then it's worth checking with sass as well and if it works there as well then I guess cra made another fix someplace else and these fixes contradict each other

I use sass module too and this is definitely not working.

_I'm glad to do some very little work for create react app, but really the repo needs a proper doc to contribue ๐Ÿ˜… I was struggling because yarn pack generate a different .tgz than the one build during the install phase. (react-scripts-v4.0.0.tgz instead of react-scripts-4.0.0.tgz without "v")_

So I reverted your commit and I had a different error but it is still here.

With the revert of your commit it has the absolute url /logo192.png :
image

Without the revert, it has the relative url ../../../../logo192.png.

But I confirm, the error remains present in any case. Thanks for your answer @atlanteh but do you have any clues on how to fix it, it seems you have a good understanding of the resolve-url-loader ๐Ÿ˜…. I want to give a try to fix the issue because we really needs it to work on our project.

I currently investigate this commit https://github.com/facebook/create-react-app/commit/1cbc6f7db62f78747cb6ca41450099181139325e from multiple authors that I can't just revert. But there are many changes on from where the file are loaded.

I just upgraded my project to use [email protected] and it still works.
Are you guys sure you deleted the entire node_modules and reinstalled? Maybe we use things differently?
My project setup is as follows:

  • In my package.json I have "node-sass-chokidar": "1.3.4",
  • I have src/index.scss file which imports styles from styles directory + ../node_modules/... directories
  • in index.js I import this file import './index.scss';
  • I have src/images folder with all kind of images in there
  • In scss files I'm using these images as follows: background: url(/images/login-bg.png) center/cover no-repeat;

This works for me both in development and in production

Upon reading the issue more thoroughly, I think that the issue is that you are referencing images from public directory, which I guess my commit doesn't allow anymore. Actually I think the new solution is better

  • Have all your images inside src/images
  • reference them in the scss files using /images/*.png
  • only the images actually used will be copied to the build /media folder rather than the entire public directory

Can you please check if this solution work for you?

@atlanteh I was facing the same issue described by others above, and as you suggested, moving the assets files used by *.scss files from /public/* to src/* fixes the issue (I've migrated from [email protected] to 4.0.0).
example:

  .header-bg {
    background-image: url('/assets/images/home-bg-black.png');
  }

This does not work:

public/
   assets/
      images/
         home-bg-black.png

After moving the files to /src:

src/
    assets/
       images/
          home-bg-black.png

Thanks!
cc @thomasleduc

for me only works after I use relative paths to my images folder (that I moved from public folder). maybe something wrong with my tsconfig?

{ "compilerOptions": { "outDir": "build/dist", "module": "esnext", "target": "ES2015", "downlevelIteration": true, "lib": [ "es6", "dom", "es2018" ], "sourceMap": true, "allowJs": true, "jsx": "react", "noFallthroughCasesInSwitch": true, "moduleResolution": "node", "rootDir": "src", "forceConsistentCasingInFileNames": true, "noImplicitReturns": true, "noImplicitThis": true, "noImplicitAny": false, "strictNullChecks": false, "suppressImplicitAnyIndexErrors": true, "noUnusedLocals": false, "allowSyntheticDefaultImports": true, "skipLibCheck": true, "esModuleInterop": true, "strict": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "experimentalDecorators": true, "baseUrl": "src" }, "include": [ "src" ] }
also I needed to include the noFallthoughtCasesInSwitch: true and changed the key the jsx from preserve to react

@mgonyan if the fix is to move all assets into the src folder that could be as simple as updating the docs to list that as a breaking change. Also updating the docs here

if the fix is to move all assets into the src folder that could be as simple as updating the docs to list that as a breaking change. Also updating the docs here

Maybe change the template as well to add some image in src/assets/images

also I needed to include the noFallthoughtCasesInSwitch: true and changed the key the jsx from preserve to react

This is already fixed on master. I'm afraid we have to wait for the next release. ยฏ_(ใƒ„)_/ยฏ
Does someone knows how to refer to master or an unpublished work on react-scripts ?

Anyway, Thanks a lot for your time @atlanteh. It is greatly appreciated ๐Ÿ‘

I thought I was going crazy. 5 mins to get the app running 2 hours of googling to fix this issue :/

Also moving images inside source is not a solution if you want common inline and sass referenced images without duplicating them

@makis-spy
First of all, I'm very sorry that you had to go to all this trouble. I guess it's on me. I always used the images in the src and things stopped working for me (and others) when trying to update to 3.4, so I found that solution and made a PR.
Regarding your concern, can you please elaborate on that?
What do you mean by "common inline images"?
If the images are small enough, then they will be inlined for you automatically.
I'm not sure I understand what difference changing the folder makes..

@atlanteh oh no worries mate!

Maybe you can shed some light. So I have a public/img folder and a src/img folder

< img src="/img/me.png" /> calls public/img

.avatar {
background:url(//img/me.png) calls src/img
}

Do I need 2 images of me ?

What I am finding works for anyone needing a quick fix is keeping 2 sets of images (ducks a tomato)

One in public for after rendering

One in source just to bypass the file not found by loader

I think your < img src="/img/me.png" /> is wrong.
You need to do it this way:
```
import MyImage from 'src/img/me.png';
...
function MyAvatar() {
return ()
}

@atlanteh If I use an image very often, say a logo that I put everywhere. It seems to me that inline this logo is not the solution even if it's small.

Say I use it 50 times in page, if I fetch the image, it would be cached in browser and reused across the all page.
If instead I inline it, my html bundle would be very huge.
I don't know if gzip would do the work on the bundle since there is a repetition; But it looks not good to me.

I think the developer should have the choice to make a http call or inline the image. don't you think ?

Again, it is not your fault, and I'm glad you're here to help :)

@thomasleduc If you import this image in a Logo component and use that component everywhere, I think everything should be ok. Since this image is only imported once, and only the Logo reference is being used everywhere, so the bundle should not be affected by the times the Logo is used.
If that's not an option, you can always disable image inlining by setting env IMAGE_INLINE_SIZE_LIMIT=0:

https://create-react-app.dev/docs/adding-images-fonts-and-files/

To reduce the number of requests to the server, importing images that are less than 10,000 bytes returns a data URI instead of a path. This applies to the following file extensions: bmp, gif, jpg, jpeg, and png. SVG files are excluded due to #1153. You can control the 10,000 byte threshold by setting the IMAGE_INLINE_SIZE_LIMIT environment variable as documented in our advanced configuration.

Ohh ok, I didn't think it this way.
Thanks a lot for all your answers and for your work @atlanteh ๐Ÿ™

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jantimon picture jantimon  ยท  154Comments

akctba picture akctba  ยท  80Comments

sgriffey picture sgriffey  ยท  113Comments

acskck picture acskck  ยท  213Comments

gaearon picture gaearon  ยท  86Comments