Background-image in CSS not working in Phoenix 1.6

Again, my problems are that:

This “loader”, whatever it is (a program, method, whatever…) of esbuild creates another copy of my existing image phoenix.png that is already here: /priv/static/images/phoenix.png. /images is a place I want to have all images that are design related - and I suppose that is standard in webdev world.

The copy is being put here: /priv/static/assets/phoenix-HEGEN2FR.png.

The suffix is probably some mechanism to prevent duplicates or something, I don’t know esbuild at all.

But I don’t want to have what I have now, which is this:

/priv
  /static
    /assets
      app.css
      app.js
      phoenix-HEGEN2FR.png   <-- I DO NOT WANT THIS HERE
    /images
      phoenix.png
    favicon.ico
    robots.txt

I want to have:

/priv
  /static
    /assets
      app.css
      app.js
    /images
      phoenix.png  <-- I WANT ONLY THIS
    favicon.ico
    robots.txt

Any idea how to do this?

How to disable the creation of duplicit files by esbuild?

I am completely stuck.

All I wanted was to show an image via background-image on my html element.

And I ended up debugging my app and trying to search through the esbuild go source code.

Please, could somebody try to install a vanilla fresh Phoenix 1.6 install and try to use background-image in the phoenix.css or app.css file (using e.g. the included Phoenix logo image in priv/static/images/phoeinx.png) without creating an additional duplicate image in priv/static/assets as I have described in more details in my previous post?

Thank you in advance.

i’ve created a fresh install in 1.6 on mac and i get the same problem

[watch] build started (change: "css/phoenix.css")
 > css/phoenix.css:41:24: error: Could not resolve "../images/phoenix.png"
    41 │   background-image: url("../images/phoenix.png");

I’ve other project where i’m experimenting with talwind and alpine and it works
maybe trying to add them to your project and remove after.
Don’t know, i’m new to this.
i’ve followed this Adding Tailwind CSS to Phoenix 1.6

1 Like

Thanks, finally somebody who can reproduce this problem on his machine. I thought I was crazy.

Perhaps the devs who are more intimate with Phoenix development will notice this post and will be able to help.

When you are serving assets to a website, they should get bundled first - this will take the raw files and output them to a separate file/directory, where they are then delivered to the client efficiently. This is what esbuild is for - it is a bundler. You can control the output directory in config.exs:

config :esbuild,
  version: "0.12.18",
  default: [
    args: ~w(js/app.js --bundle --target=es2016 --outdir=../priv/static/assets),
    cd: Path.expand("../assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

Notice --outdir=../priv/static/assets

I have already images in my static folder. Again, I don’t want to duplicate my images.

Now I have dupllicates (pheonix.png and phoenix-HEGEN2FR.png are duplicates of the same file):

/priv
  /static
    /assets
      app.css
      app.js
      phoenix-HEGEN2FR.png   <-- I DO NOT WANT THIS HERE
    /images
      phoenix.png
    favicon.ico
    robots.txt

I want to have:

/priv
  /static
    /assets
      app.css
      app.js
    /images
      phoenix.png  <-- I WANT ONLY THIS
    favicon.ico
    robots.txt

/priv/static is an output dir, not a source dir. likewise, it is in the .gitignore file. You images should live in /assets/static/images/, and something in your build pipeline should copy them into /priv/static/images/

Your css should looks like /assets/css/app.css. So it should reference the images with relative dir like ../static/images/bluh.jpg

During the build process the images will be copied, the css will be built, and part of the building is to fix up the file reference to point to the final dir.

Then why is the default phoenix.png - distributed by the phoenix team image in /priv/static/images/phoenix.png and not in /assets/static/images/phoenix.png as you are suggesting?

Check for yourself if you don’t believe me. Install the Phoenix 1.6 and check the folder and files structure.

That used to be the case pre 1.6. In 1.6 only priv/static/assets are ignored and static assets are indeed meant to go into priv/static directly.

2 Likes

Any idea how to make esbuild stop making copies of the already-existing images in priv/static/images ? Is there any option to ignore budling of images and just leaving them as they are, that is, allowing to linking to them in CSS in background-image with the old way as in Phoenix 1.5?

Haven’t try 1.6 dir structure so I can judge whether I like it or not. One can always use the old dir structure if the new one don’t work out.

We have been looking at it. Just a quick note – I understand that you are currently frustrated that something is causing you problems, but your tone to multiple people trying to help you out is off-putting. It it difficult to find motivation to approach this issue after reading the thread :frowning_face: Please keep that in mind when asking for help and replying to others providing help.

That said, we have a solution that will fit your use case. Before we get there, one other note, for you and future readers: generally the reason that assets like images get duplicated is for the purpose of cache busting, as in the process by which we can tell client browsers to cache assets that rarely change for long periods of time and still allow those assets to be refreshed in a timely manner when they may change. Usually this is done by appending a hash of the file contents to the filename. This necessarily creates (at least) a duplication of your cached-busted (i.e. digested) assets.

So the behaviour you are seeing is esbuild trying to help you out by cache-busting your assets. However it sounds like you may not want that, so here is how you disable it in esbuild.

In your config/config.exs, find the esbuild configuration. It should look something like this:

config :esbuild,
  version: "0.12.18",
  default: [
    args:
      ~w(js/app.js --bundle --target=es2016 --outdir=../priv/static/assets),
    cd: Path.expand("../assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

append the external option to the list of args, so it looks like this:

args:
      ~w(js/app.js --bundle --target=es2016 --outdir=../priv/static/assets --external:/images/*)

Now, when you reference your image paths in css, do so with absolute paths just like you would in the browser:

background-image: url("/images/phoenix.png");

and that’s it.

Now let’s say you do want cache-busting. Phoenix has you covered. Run mix assets.deploy, which is an alias in the generated mix.exs from the Phoenix installer. It runs the phx.digest command which will handle cache-busting for all of your assets in priv/static. To remove the digested assets, you can run mix phx.digest.clean --all.

Let us know how it goes!

12 Likes

Wow, thanks. Exactly what I wanted. This --external:/images/* option was all it needed. And as a side bonus I don’t need to now have --loader:.jpg=file --loader:.png=file --loader:.gif=file --loader:.svg=file in dev.exs. At least it works after I removed it, saved and restarted the server.

So, thanks again.

About my behavior, if you look at the answer from people except you, of course, and the one guy who reproduced my problem, all of them were suggesting me to learn esbuild or look into docs but if I asked them if they can be a little bit more specific they didn’t respond or they just told me that the question has already been answered and generally not very helpful things. And I think we all know that no one of them knew about the elegant --external:/images/* solution of yours which would also eliminate the weird ../../priv/static/images/blue.png path in the url() in the CSS file and it also removed the necessity of those --loaders options in dev.exs.

Anyways, thanks for your help.

My guess is this could be somehow explained further in the official Phoenix docs, because I really don’t think I will be the only one who will be very surprised that images in background-image don’t work.

I can only speak for myself, but linking to docs/resouces is usually done quite a bit quicker than providing actual specifics. The github link I provided did use the --external flag, but I didn’t have the time nor project at hand to actually try the flag out to provide a working solution directly.

I think as well, that this should be the default. Cache busting is already provided through mix phx.digest.

1 Like

But did you tell them you read all the docs and learned esbuild? Esbuild is quite new so not many people (at this forum) will have in depth experience. So all they can do is point in a direction. It’s up to you to go have a look and report if/when found an answer.

And in my experience: you will never lose even when you can’t find the answer in docs. Cause even then you have learned a lot by looking though them (so you learned a bit about esbuild) which will improve your broader knowledge.

There is a saying about a man and a fish. Don’t exactly know how it goes; but here you have a direction :slight_smile:

2 Likes

Dude I didn’t even know that flag existed until we started looking into this. So as much as it might not be what you wanted to hear, “check out the esbuild docs” ended up being the correct solution. :slight_smile:

I agree, and I have opened a PR :slight_smile: Add external flag to esbuild for fonts and images by mcrumm · Pull Request #4536 · phoenixframework/phoenix · GitHub

Please try to remember that we are all people, and for the most part all of the work happening here is volunteerism. Sometimes we miss stuff. Sometimes we find better solutions later. It’s all iterative. Have a good one!

8 Likes

I actually have a question wrt. cache busting in this situation. Yes, mix will digest those files and add tagged versions, however, if my css (untouched by mix anyway) is still referencing the plain images/blue.png, how exactly does the cache busting work then?

1 Like

The digested css gets built with the digested image/font/etc. paths (I actually had to go verify this manually for myself because I had the same question)

EDIT: To be clear, I am saying that if you run mix phx.digest and in your templates you are using the static_path(...) helper, then you are serving the digested app-[hash].css file, and that file has url() paths that point to the other digested assets: (images/blue-[hash].png)

1 Like

Do you mean mix phx.digestwill mung the content of the css, by expanding a macro like url(...)? Where is this documented? I want to know more.

Check the source for Phoenix.Digester– that is what is invoked when you run mix phx.digest.

1 Like