4

This is an image srcset generated by Gatsby:

<source type="image/webp" srcset="
/static/be21c7dd0d11c74c18530fc39aefa922/10917/mailboxes.webp 200w,
/static/be21c7dd0d11c74c18530fc39aefa922/21f2d/mailboxes.webp 400w,
/static/be21c7dd0d11c74c18530fc39aefa922/fdc6a/mailboxes.webp 800w,
/static/be21c7dd0d11c74c18530fc39aefa922/e7f3d/mailboxes.webp 1200w,
/static/be21c7dd0d11c74c18530fc39aefa922/faacb/mailboxes.webp 1600w,
/static/be21c7dd0d11c74c18530fc39aefa922/acdd2/mailboxes.webp 1800w" 
sizes="(max-width: 800px) 100vw, 800px">

My image is displayed in a container that is at most 800px wide, so this is how I would prefer the browser to choose:

if screenWidth > 400 then choose 800w
if screenWidth > 200 then choose 400w
else choose 200w

However, the browser is actually always choosing the largest possible image (even if I resize the browser to 200 width).

I believe the problem is here:

sizes="(max-width: 800px) 100vw, 800px"

There should be like ~3 conditions, right? Instead there is only 1 condition. I'm not sure I'm even able to interpret what this condition is trying to ask the browser to do.

Below is my GraphQL code:

edges {
    node {
      excerpt
      fields {
        slug
        prefix
      }
      frontmatter {
        title
        tags
        cover {
          children {
            ... on ImageSharp {
              fluid(maxWidth: 800, maxHeight: 360, traceSVG: { color: "#f9ebd2" }) {
                ...GatsbyImageSharpFluid_withWebp_tracedSVG
              }
            }
          }
        }
      }
    }
  }

The fragment ...GatsbyImageSharpFluid_withWebp_tracedSVG generates srcset and sizes. In the documentation there isn't anything I could do to affect how they are generated. However, I could potentially manipulate them at a later point:

<Picture fluid={fluid} />

Should I manipulate fluid.sizes at this point, or is there a less dirty way to fix the srcset?

Atte Juvonen
  • 4,922
  • 7
  • 46
  • 89

2 Answers2

6

It turns out a high resolution image was loaded because I was using a device which has high DPI. So it was working as intended.

A web browser applies devicePixelRatio to its calculations when choosing which image to show. For example, if devicePixelRatio == 2, then the device has 2 times the amount of typical pixels (relative to physical width of the device and the physical distance to the user). When a browser wants to render a "800 pixel wide" image on this device, it actually needs a 1600 pixel wide image to make it look good on this high DPI display.

The browser was in fact following the sizes rule. Adapted from Derek's answer:

(max-width: 800px) 100vw   -> If viewport < 800px, use (100vw * devicePixelRatio)
800px                      -> By default use (800px * devicePixelRatio)
Atte Juvonen
  • 4,922
  • 7
  • 46
  • 89
  • right, I forgot about the pixel ratio -- it indeed explains why image larger than 800px is loaded. Only 1 caveat, I don't believe browser will multiply 800px by devicePixelRatio for the default case -- px is an absolute unit. BTW, I think you should keep the `gatsby` tag — half of the question is about passing in custom option for gatsby-plugin-sharp :) – Derek Nguyen Mar 01 '19 at 06:04
  • 1
    Ok, edited tags. The browser actually does multiply 800px by devicePixelRatio; that's the behavior I'm seeing when max-width is larger than 800px (the default case). – Atte Juvonen Mar 01 '19 at 12:58
  • Interesting! I’ll try it out. Thank you for the interesting question, cheers – Derek Nguyen Mar 01 '19 at 13:12
2

You don't have to manually modify the generated fluid object, you can pass in an option when querying images like so:

{
  file(ext: {
    eq: ".png"
  }) {
    childImageSharp {
      fluid(maxWidth: 800, sizes: "(min-width: 400px) 800px ,(min-width: 200px) 400px, 200px") {
        srcSet
        sizes
      }
    }
  }
}

sizes="(max-width: 800px) 100vw, 800px"

This is the default sizes generated by gatsby-plugin-sharp for fluid image. It has 2 rules:

(max-width: 800px) 100vw   -> If viewport < 800px, use 100vw
800px                      -> By default use 800px

So browser should... always use 800px? Your rule maybe should be like this:

(min-width: 400px) 800px, <-- if > 400, use 800px
(min-width: 200px) 400px, <-- if > 200, use 400px
200px <-- else use 200px

Some resources:

Responsive Images Done Right: A Guide To And srcset

gatsby-plugin-sharp | default sizes

Derek Nguyen
  • 11,294
  • 1
  • 40
  • 64