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

gatsby-image rendering larger than maxWidth (fluid) #15669

Closed
daveyheuser opened this issue Jul 12, 2019 · 16 comments
Closed

gatsby-image rendering larger than maxWidth (fluid) #15669

daveyheuser opened this issue Jul 12, 2019 · 16 comments
Labels
stale? Issue that may be closed soon due to the original author not responding any more. type: question or discussion Issue discussing or asking a question about Gatsby

Comments

@daveyheuser
Copy link

daveyheuser commented Jul 12, 2019

Description

Gatsby-image is rendering images larger than their maxWidth and Height set in graphql. This is the query I've used:

appUI: file(relativePath: {eq: "app@2x.png"}) {
  childImageSharp {
    fluid(maxWidth: 1062, maxHeight: 723 ,quality: 100) {
      ...GatsbyImageSharpFluid_withWebp
    }
  }
}
<Img fluid={data.appUI.childImageSharp.fluid} alt='IconJar app UI' />

Expected result

I expect the rendered image to not be larger than 1062px in width, nor greater than 723px in height. However, it's rendered at 1168px. The container its in is 1200px in width, so 1168px feels random.

This is what's rendered, which doesn't mention the width of 1168px anywhere 🤔

<div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;">
  <div style="width: 100%; padding-bottom: 68.0451%;"></div>
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsSAAALEgHS3X78AAACs0lEQVQ4y6VTyXLTQBA1y6/wI1yTorjyGfmIVFHcOHDOFXzgwCEcwoGEUMSOg3cHO7YWW7KtxdYu2ZY0zesR8ANM1auZaWlev349U7v78fV5d9B+0e10j7rdzvFoODy+v78/Ho/HEkPsGaPRSIK/MSaTidw3G42jQa/z8u6u9azGYzL4uTbXBllrixzHoSiKKQhDStOUEqAsSyqKgvI8lzgcDhJRFJFlWWTZDiV7QePx5I0kNMyV4dguue6mwI/CNE0Rx7EAuVitVkJRFKHrusBhsV6vRRiGMr7fg4VIFEWZYyZV0ypCVdUM13Whzi2TJBVryxFxkgjP8wTiMgETbDYbEFrCDwKxXK4kGcdPT0/zNEkIsYpwOp2ZNmSDoIzilB5UlO/6tEY5y+WSptMpKYpKOCxnVEKaplEQBHR2dkYnJyd5s9kkXZ9XhFiYUEdQUO52B9JNizZeSKx6u90SyiTbtjmhBHvn+75co1mEBuU6EqiKWhHe3DTMdrtNrVarnM/nZJim/JmJuEmLhSHVcQIkJXgoVe73B8p2O0qzTHo4/duU29uW2e8PCCgXiwUxKSuAd5KAybibnGQDxUzI8ThOMG8p8IOcDX14mFaEIDC5JPhYctY5SNkf9o9LZjL2mJMwcAPk9zDEteHbsfGkwtlsVhHOZgrELLmMMssyUlVVqmCFnIgVs3JWZTsVsePYFKV7Gqoe/VpEOTNqqvKPcG4YBtR4B1ybAsoKKCjgYYHDBbwrUJqMwdMCyeScphnWcYEze6pKfi0JNU33+XV4ns9x6RW/jC1mfhGsegfzOcalCiFkp3lk2Q57ucT1mr2VhBcXX94BH6+uvn1Ap+uDwUCi3+/XO91evder0Ol0/q3RwPr19ff65eVVvdFovsfZT+fnn1/V/ozHwNP/xBPgEYTWfgMTvniypuoWLwAAAABJRU5ErkJggg==" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0;">
  <picture>
    <source type="image/webp" srcset="/static/9a8c4ba3947922d60663886f7b19dae2/be27e/app%402x.webp 266w, /static/9a8c4ba3947922d60663886f7b19dae2/83788/app%402x.webp 531w, /static/9a8c4ba3947922d60663886f7b19dae2/555c8/app%402x.webp 1062w, /static/9a8c4ba3947922d60663886f7b19dae2/59196/app%402x.webp 1593w, /static/9a8c4ba3947922d60663886f7b19dae2/c6a81/app%402x.webp 2124w" sizes="(max-width: 1062px) 100vw, 1062px">
    <source srcset="/static/9a8c4ba3947922d60663886f7b19dae2/90a8d/app%402x.png 266w, /static/9a8c4ba3947922d60663886f7b19dae2/3c895/app%402x.png 531w, /static/9a8c4ba3947922d60663886f7b19dae2/86980/app%402x.png 1062w, /static/9a8c4ba3947922d60663886f7b19dae2/ed50d/app%402x.png 1593w, /static/9a8c4ba3947922d60663886f7b19dae2/90d6a/app%402x.png 2124w" sizes="(max-width: 1062px) 100vw, 1062px">
    <img sizes="(max-width: 1062px) 100vw, 1062px" srcset="/static/9a8c4ba3947922d60663886f7b19dae2/90a8d/app%402x.png 266w, /static/9a8c4ba3947922d60663886f7b19dae2/3c895/app%402x.png 531w, /static/9a8c4ba3947922d60663886f7b19dae2/86980/app%402x.png 1062w, /static/9a8c4ba3947922d60663886f7b19dae2/ed50d/app%402x.png 1593w, /static/9a8c4ba3947922d60663886f7b19dae2/90d6a/app%402x.png 2124w" src="/static/9a8c4ba3947922d60663886f7b19dae2/86980/app%402x.png" alt="IconJar app UI" loading="lazy" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: none 0s ease 0s;">
  </picture>
  <noscript>
    <picture>
      <source type='image/webp' srcset="/static/9a8c4ba3947922d60663886f7b19dae2/be27e/app%402x.webp 266w, /static/9a8c4ba3947922d60663886f7b19dae2/83788/app%402x.webp 531w, /static/9a8c4ba3947922d60663886f7b19dae2/555c8/app%402x.webp 1062w, /static/9a8c4ba3947922d60663886f7b19dae2/59196/app%402x.webp 1593w, /static/9a8c4ba3947922d60663886f7b19dae2/c6a81/app%402x.webp 2124w" sizes="(max-width: 1062px) 100vw, 1062px" />
      <source srcset="/static/9a8c4ba3947922d60663886f7b19dae2/90a8d/app%402x.png 266w, /static/9a8c4ba3947922d60663886f7b19dae2/3c895/app%402x.png 531w, /static/9a8c4ba3947922d60663886f7b19dae2/86980/app%402x.png 1062w, /static/9a8c4ba3947922d60663886f7b19dae2/ed50d/app%402x.png 1593w, /static/9a8c4ba3947922d60663886f7b19dae2/90d6a/app%402x.png 2124w" sizes="(max-width: 1062px) 100vw, 1062px" />
      <img loading="lazy" sizes="(max-width: 1062px) 100vw, 1062px" srcset="/static/9a8c4ba3947922d60663886f7b19dae2/90a8d/app%402x.png 266w, /static/9a8c4ba3947922d60663886f7b19dae2/3c895/app%402x.png 531w, /static/9a8c4ba3947922d60663886f7b19dae2/86980/app%402x.png 1062w, /static/9a8c4ba3947922d60663886f7b19dae2/ed50d/app%402x.png 1593w, /static/9a8c4ba3947922d60663886f7b19dae2/90d6a/app%402x.png 2124w" src="/static/9a8c4ba3947922d60663886f7b19dae2/86980/app%402x.png" alt="IconJar app UI" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/>
    </picture>
  </noscript>
</div>

image

image

Environment


  System:
    OS: macOS 10.14.5
    CPU: (8) x64 Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz
    Shell: 5.3 - /bin/zsh
  Binaries:
    Node: 11.10.0 - /usr/local/bin/node
    Yarn: 1.13.0 - /usr/local/bin/yarn
    npm: 6.9.2 - /usr/local/bin/npm
  Languages:
    Python: 2.7.10 - /usr/bin/python
  Browsers:
    Chrome: 75.0.3770.100
    Safari: 12.1.1
  npmPackages:
    gatsby: ^2.12.0 => 2.13.19 
    gatsby-cli: ^2.7.9 => 2.7.13 
    gatsby-image: ^2.2.4 => 2.2.6 
    gatsby-plugin-dark-mode: ^1.1.0 => 1.1.0 
    gatsby-plugin-manifest: ^2.2.0 => 2.2.3 
    gatsby-plugin-netlify: ^2.1.0 => 2.1.2 
    gatsby-plugin-react-helmet: ^3.1.0 => 3.1.2 
    gatsby-plugin-segment-js: ^3.0.1 => 3.0.1 
    gatsby-plugin-sharp: ^2.2.2 => 2.2.6 
    gatsby-plugin-styled-components: ^3.1.0 => 3.1.2 
    gatsby-source-filesystem: ^2.1.1 => 2.1.5 
    gatsby-transformer-json: ^2.2.0 => 2.2.2 
    gatsby-transformer-sharp: ^2.2.1 => 2.2.3 
  npmGlobalPackages:
    gatsby-cli: 2.7.7
@pieh
Copy link
Contributor

pieh commented Jul 13, 2019

Please check https://www.gatsbyjs.org/packages/gatsby-image/#avoiding-stretched-images-using-the-fluid-type

We don't limit width by default - there is the need of user choice in how to layout images that are narrower than container (should they be center, left-aligned, right-aligned).

It also might be desired not to limit width in cases like hero images or other full-viewport width images and it's easier to use max-width or any alternative method to limit width, than to apply limit by default and let user try to unset it.

@pieh pieh added the type: question or discussion Issue discussing or asking a question about Gatsby label Jul 13, 2019
@slorber
Copy link
Contributor

slorber commented Aug 1, 2019

hi,

I'm sorry @pieh but I'm totally unable to understand all how this works after reading the doc.

What I can tell is:

  • I have a source image of 4000w

  • I use this query:

const useSliderFluidImages = (): FluidObject[] => {
  const data: any = useStaticQuery(
    graphql`
      query {
        allFile(
          sort: { fields: name, order: ASC }
          filter: { relativeDirectory: { eq: "home/hero" } }
        ) {
          edges {
            node {
              id
              name
              childImageSharp {
                fluid(maxWidth: 1000) {
                  ...GatsbyImageSharpFluid_withWebp_tracedSVG
                }
              }
            }
          }
        }
      }
    `,
  );
  const fluids = data.allFile.edges.map(
    (edge: any) => edge.node.childImageSharp.fluid,
  );

  console.debug('fluids',fluids);
  return fluids;
};
  • I look at the static output (after having cleaned the folder just before)

image

  • I see there are images of size 2000 + 4000, in webp + jpeg

  • I see images of 2000 + 4000 in srcset

image


Is there a way to ensure that no matter what we only use an image of width === 1000?
I use a high density screen, yet I really want to NEVER use an image larger than 1000px, for perf reasons. I don't want anything bigger than 1000px in the srcset output. Actually I don't even want the original size be in the srcset output. I have very large original files (yes I know it can affect the build time), yet if user has a huge screen I don't want to serve 10mo images. Particularly when this picture is served under an opacity overlay/mask where quality required is less important.

I don't understand the things about streching images. I'm just using object-fit so I don't understand how I could be concerned by this. It's totally possible to display a smaller image in a large container and yet preserve image ratio.

I have a fluid hero image, the container might be 4000px, yet I still want to limit the image displayed inside this container to be max width = 1000px, even if quality is not good, it's my own problem.

Can you please tell me what I can do to really put a threshold of 1000pw on my images? Currently people having large screens are downloading 1.5mo of hero image.

The only workaround I see for now is... to actually reduce my original image sizes. That feels wrong no?

@KyleAMathews
Copy link
Contributor

No browser will download the 4000px image — if you have your HTML container set to a maxWidth of 1000px — then a non-retina screen will download the 1000w image and a retina will download the 2000px — but this does suggest there's not much point in including larger images if they're more than 2x the maxWidth. The reason the original is added is often people will query for an image that isn't at least 2x the maxWidth so we want to make sure the largest possible image is included.

@slorber
Copy link
Contributor

slorber commented Aug 4, 2019

So, on Twitter @KyleAMathews found I had good arguments (https://twitter.com/gatsbyjs/status/1157067242858614785)

I understand the rationale behind always outputting x2 the maxWidth for retina screens, yet there are 2 problems:

  • The original image size is always in the output. If my original is 6000w and I put maxWidth 1000w, a retina screen of 2001w will take the 6000w picture
  • There's no good way to cap the width of images

Let's consider 2 scenarios to understand better. Both assume my input image is 6000w:

1 small fluid thumbnail where container to width=500

  • We use maxWidth = 500
  • We'll get 500, 1000 and 6000 in the srcset, (+ smaller breakpoints)
  • As the container is limited to 500px, the browser will use 500 or 1000 depending on pixel density

2 large fluid hero image 100vw

  • We use maxWidth = 2000, because 2000 is already quite nice and heavy and we don't want user to download 1mo+ images
  • We get in srcset 2000, 4000, 6000 (+ smaller breakpoints)
  • Retina screen of width 2001 picks the 6000 picture

This is a big problem for me because this means a large retina screen will download a huge picture and create a lot of work for the browser to resize it, giving bad lighthouse score. I should have control over the output of Gatsby's srcset. In my usecase if user has a retina screen of 2001w, I prefer him to use the 2000w image, not the 6000w image, even if the image won't be as beautiful as least it will be more lightweight. (In my case this hero image is behind a translucide overlay so I don't require extreme quality)

Proposal

It would be nice to be able to tell gatsby which is the hard limit for image size width to be in the output resizes/srcset, without loosing the default ability to handle retina screens by default using current x2 strategy.

I'm thinking of something like this

  • fluid(maxWidth: 2000) => will output 2000 and 4000 (x2), but not 6000 (original image width)
  • fluid(maxWidth: 2000, maxWidthHighDensity: 2000) => will output 2000
  • fluid(maxWidth: 2000, maxWidthHighDensity: 3000) => will output 2000 and 3000

@KyleAMathews
Copy link
Contributor

I think the simplest solution is to only include the original image if it's smaller than 2 * maxWidth.

My assumption with the code is that you're always constraining the width of the image but this isn't true with 100vw so we should check the size of the original image before including it in the srcset

@slorber
Copy link
Contributor

slorber commented Aug 4, 2019

Filtering by 2 * maxWidth would already be nicer yeah, but I would rather give user full control and let him set a hard limit which defaults to filtering on x 2

Yes that's true I may normally not want to a put a maxWidth on my hero images as the width of the container has an unlimited maxWidth. Yet If user has a retina screen of 2000w+ I don't want the srcset to load a 4000 image neither as it's really heavy for both bandwidth and resizing. If screen is retina 2200w I'd rather have the browser download the 2000w version even if this means lower quality (which is fine for my specific usecase).

The solution you are proposing does not really permit me to put a hard limit of 2000 in srcset, and I'll have to put 1000 as a workaround for my usecase which is a bit counter intuitive as an API.

@KyleAMathews
Copy link
Contributor

KyleAMathews commented Aug 4, 2019

maxWidth is just the max width of screens you care about for the design. Most people don't design past say 1400 px so that's what I'd assume they'd put. We're trying to simplify your life so you don't have to think about different types of screens and image sizes. You just ask for what your design needs.

@slorber
Copy link
Contributor

slorber commented Aug 4, 2019

Yeah got that but what I'm supposed to do? I want 2000 non retina to get 2000px image, and 2000 retina to also get 2000px image. Current API does not really allow to me tell Gatsby those requirements. I understand your default choices, but it would be cool to be able to override those if they don't make sense for my usecase

@KyleAMathews
Copy link
Contributor

Once we make the change to exclude the original, that's what would happen with a maxWidth of 1000. Browsers would see the max image is 2000 pixels and that the screen width is 2000 and grab the largest.

@slorber
Copy link
Contributor

slorber commented Aug 5, 2019

Sure, that'll do the job for my case, but still think it's a bit counter intuitive api ;)

@baobabKoodaa
Copy link
Contributor

I just ran into this same issue. For me the problem is that the website becomes unfathomably large when unnecessarily large versions of images are generated (I don't know how large; it takes too long to compile). I understand all the arguments regarding high retina displays and letting the browser choose etc. -- but I can't compile my site without resizing images outside Gatsby. How about keep the default behavior as it is and offer users a flag like nothingLargerThanMax=true (with a better name obviously)?

@gatsbot
Copy link

gatsbot bot commented Sep 4, 2019

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 💪💜

@gatsbot gatsbot bot added the stale? Issue that may be closed soon due to the original author not responding any more. label Sep 4, 2019
@gatsbot
Copy link

gatsbot bot commented Sep 15, 2019

Hey again!

It’s been 30 days since anything happened on this issue, so our friendly neighborhood robot (that’s me!) is going to close it.

Please keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m HUMAN_EMOTION_SORRY. Please feel free to reopen this issue or create a new one if you need anything else.

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks again for being part of the Gatsby community!

@gatsbot gatsbot bot closed this as completed Sep 15, 2019
@steverandy
Copy link

Is there a plan to exclude the original size?

@maxcct
Copy link

maxcct commented May 6, 2020

Would also greatly appreciate this: getting Lighthouse failures for dom-size and uses-responsive-images because I can't stop the original size images coming through.

@deldeldeldeldel
Copy link

deldeldeldeldel commented Nov 6, 2020

it can be helpful to resize images: https://www.gatsbyjs.com/docs/preoptimizing-images/
but i don't know how to use it properly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale? Issue that may be closed soon due to the original author not responding any more. type: question or discussion Issue discussing or asking a question about Gatsby
Projects
None yet
Development

No branches or pull requests

8 participants