Niels-IO / next-image-export-optimizer

Use Next.js advanced <Image/> component with the static export functionality. Optimizes all static images in an additional step after the Next.js static export.
413 stars 51 forks source link

Srcset not created for Static Images #198

Closed el-j closed 5 months ago

el-j commented 5 months ago

Hej i really love the idea of this package.

anyways sadly just get one 1x and 2x srcset attribute both for the biggest version of my image. it does not load smaller versions on smaller screensizes.

i am wondering if it is because of how i use the static images? as i have them all in my public folder and i do not want to hand in any width or height parameters as u show in the example: image

i tried to trick this importing a bit by importing all my images into the header of an "Images.tsx" and later choose them from the image-src i get into the component. i hand this then to src of ExportedImage component.

import staticimage from  "../public/staticiamage.png"
import staticimage2 from  "../public/staticiamage2.png"

const images = { staticimage, staticimage2 } as { [key: string]: StaticImageData };

export const Images = ({
src
}: {
  src: string;
}) => {
  let theImage = images[src];
  return (
    <ExportedImage
      src={theImage}
      alt={src}
      loading="lazy"
      basePath={
        process.env.NEXT_PUBLIC_BASE_PATH
          ? process.env.NEXT_PUBLIC_BASE_PATH
          : ""
      }
    />
  );
};

the output is then e.g.

<img alt="staticimage" 
loading="lazy" 
width="1920" 
height="1080" 
decoding="async" 
data-nimg="1" 
srcset="/nextImageExportOptimizer/staticimage.a31d2aae-opt-1920.WEBP 1x,
 /nextImageExportOptimizer/staticimage.a31d2aae-opt-1920.WEBP 2x" 
src="http://localhost:3000/nextImageExportOptimizer/staticimage.a31d2aae-opt-1920.WEBP" 
style="color: transparent;" >

when i load i get in source first a load of the smallest variant (like: opt-10.WEBP) then i load the biggest version. the other versions are all created from the export command, but they are not mentioned in the srcset attribute in the html.

do you have any clue how i can get a nice srcset from my images?

el-j commented 5 months ago

any help would be much appreciated as i want to put the page online but suffering form this problem a lot. maybe i do anything wrong? from you working online example i saw that, the width and height of the container and the width and height of the image is changed when dragging the slider.

so does it mean i need a width and height in px on container and image? then i got the idea of this package wrong, maybe?

Niels-IO commented 5 months ago

Hi @el-j,

I tested your setup in the example app in this repository. It works, and I get all the different sizes in the srcset. Can you create a minimal reproduction so that I can debug it?

When you use the statically imported images, you do not have to specify any width or height.

terijaki commented 5 months ago

I'm having the same problem (https://github.com/terijaki/vcmuellheim)

Optimized Images are generated fine with "next dev" but are not present with "next build && next-image-export-optimizer"

Note: I'm not using && next export since this is deprecated in Next14 and cause build failures: https://nextjs.org/docs/advanced-features/static-html-export

Niels-IO commented 5 months ago

@terijaki Can you point me to a specific code section of your repo where it doesn't work?

terijaki commented 5 months ago

@Niels-IO ofc

another area is a picture gallery:

terijaki commented 5 months ago

Damn! I am using https://github.com/actions/configure-pages/ during my build and deploy process. I didn't notice before that it disables image optimisation. Completely messes things up.

image
Niels-IO commented 5 months ago

Hi @terijaki,

Is it now working as expected?

I noticed two things in your use of the library:

  1. When you specify a width and height, you are telling Next.js that this is the size you want it to be displayed. So naturally, it omits the other sizes in the srcset and only uses the requested size in the image element (or the nearest larger size in the sizes array). If you want to have a dynamic behavior, you can specify fill, and then it will generate the complete srcset.

If you take this example of your website: https://github.com/terijaki/vcmuellheim/blob/1728c28829fad49e30b97f25567f438b8445e20e/app/components/homepage/HomeIntro.tsx#L21-L28 and remove the unoptimized prop, you get the expected behavior.

  1. You are using unoptimized in several places, any reasons for that?
terijaki commented 5 months ago

@Niels-IO it is working as expected in most places yes. 🤗

I have totally forgotten about the unoptimised parameter.... I've added it to the images on the homepage above the fold because I didn't like the blur effect. That being said the issue was also that the optimised webp images were not being used, so the blur effect was display too long, until the large jpgs were loaded.

Not that things are working, I'll go over every place to review what I've done this past month in terms of specific sizes and unoptimised flags 😅

In some places it is intended for sure (sponsor/brand logos). Other area's are ok-ish but not perfect yet.

Thanks for your help, creating and maintaining this tool!

Niels-IO commented 5 months ago

@terijaki Thanks a lot! Nice to hear that it is useful!

Let me know if you come across a situation again where it is not working as you would expect.

el-j commented 5 months ago

@terijaki i tried to get this work but sadly without luck. if i put the fill prop the srcset is created. but i cannot use fill as it destroys my layout. here is the running export build as pre-release.

maybe u have any idea why this is? https://el-j.github.io/fabianalthaus.de

otherwise, can give me the link to the codesandbox where u tried the example?

terijaki commented 5 months ago

@el-j the fill property can be tricky to work with but once you got the hang of it, this is the way to go. btw this has nothing to do with Niels-IO work as it using the NextJs image component.

See: https://nextjs.org/docs/pages/api-reference/components/image#fill

The parent element must assign position: "relative", position: "fixed", or position: "absolute" style.

In some situations it might be a valid solution to put a wrapper around your images and set their position to relative.

el-j commented 5 months ago

@terijaki indeed i tried with a wrapper to trick the fill prop but this does not work for me. so i am in need for the srcSet get created correctly. but it does not as u see from the exported-code. i am wondering where i can find the example @Niels-IO is talking about, as i would like to check against my code.

Niels-IO commented 5 months ago

Hi @el-j,

here is a good section of the Next.js docs to understand responsive images: https://nextjs.org/docs/pages/api-reference/components/image#responsive-images

I hope that helps for your use case

el-j commented 5 months ago

hi @Niels-IO no it does not. if it would help i wouldn't have created this issue. i have test and tried all these, without getting a good srcSet. so in fact with fill, and wrapper it just breaks the whole layout so its not a solution.

so any idea how i get the srcSet written to the output dom? i mean... all the versions of images are created and sitting there... just the image-srcset does not know them so cannot use them.

Niels-IO commented 5 months ago

@el-j
I think there is very likely a CSS solution that allows you to use any solution in the previous link and won't break your layout.

Without access to the source code of your page, there is nothing I can do.

el-j commented 5 months ago

hi again, so i created a codesandbox that shows the issue. for me it just seems the srcSet is just ignored ...

from my point of view, it should not load the 1080p variant on a size of 100px'ish width...

sandbox

myproject-issue:

image

from codesandbox issue is the same:

image
el-j commented 5 months ago

and as i see from my own screenshots now, in my project for every source size it's always the 1080p file in the attributes, in the codesandbox the list is correct, but not correctly used...

Niels-IO commented 5 months ago

Hi @el-j,

thanks for the codesandbox. It works as expected:

image

When you load a page with that small width, the correct src is the smallest source in the deviceSizes array. Which is 640px.

The behavior of most browsers is that once you load a larger image file (in the case you had the browser larger at one point and then made it smaller) that it just uses the larger image.

el-j commented 5 months ago

that is so strange...when i reload the codesandbox it really works. but if i do it with my project i just have always the biggest version loaded after the opt-10 version. does not matter which view-port size i have...

when i investigate deeper the srcSet always have the same sizes for all variantes. build on github:

image

local dev:

image

@Niels-IO u have any idea why the list of srcSet always contains just the biggest version for every size?

Niels-IO commented 5 months ago

@el-j Can you screenshot how you are using the ExportedImage component?

el-j commented 5 months ago

@Niels-IO here is the screenshot. it's not so clean as i sadly have to transfrom the incoming string to match the correct image from the staticImagesObject . image

the image-object is ontop of this code like:

import "foo" from "../imagepath/foo.jpg"
import "bar" from "../imagepath/bar.jpg"
const staticImagesObject = { foo, bar } as { [key: string]: ExportedImageProps["src"] };
image

so everywhere where i need an image. i use the Images-Component and give it a "src" attribute that chooses the right image from the staticImagesObject and uses that for the ExportedImage-Component that is returned

Niels-IO commented 5 months ago

@el-j What would happen if you remove the wrapping div? I think the bug might be related to CSS styling

el-j commented 5 months ago

@Niels-IO then i am back where i was before with the same result: all srcset variants contain the biggest version.

Niels-IO commented 5 months ago

@el-j I would recommend to start with a fresh page, add one image and see if this works. Then continuously add the complexity that you currently have in your code and see where it breaks.

el-j commented 5 months ago

@Niels-IO so now i made a dev-page. ueing my Images component and the ExporteImage component:

Screenshot 2024-01-30 at 17 45 19

which results in the same behaviour:

image

any idea? i dont understand why this is not working in my case.

Niels-IO commented 5 months ago

That should work as you have it. Can you send a screenshot from the next.config.js please?

el-j commented 5 months ago

this is my config:

Screenshot 2024-01-31 at 00 06 21

and meanwhile i was so keen to investigate what u are doing and stumbled about these comment lines: https://github.com/Niels-IO/next-image-export-optimizer/blob/c20cf5cabd33f581408a62a44c65bf9882fc6734/src/optimizeImages.ts#L225

could this be a reason? sorry i am guessing wild ... ooh... i have little idea what could be a reason ... tinkering

el-j commented 5 months ago

@Niels-IO ok sooooo: i moved my images all into the project, out of the public folder. and TADA it works.

the second line in the code example from the docs doing import me from '../photos/me.jpg' nextjs wants them to be in the src-folder if u want them static... hmpf

image

they should add this line to the docs... i really appreciate your help! thank u very much! u have done a very helpful package here!