Image Component
The image component has a lot of baked-in performance and accessibility
benefits, and should be used over a normal <img />
element wherever
possible.
Lazy Loading
Image
includes automatic lazy loading. This means that images that are not
in the user's viewport will not be loaded. Instead, a low-quality placeholder
(LQIP) version of the full-size image will be displayed until the user
scrolls the image in to view. Once the image is in view, the full-size image
will begin to download, and then it will replace the LQIP.
The sizes
prop
We use an image sizing method that can deliver cropped images on the fly by
passing width
and height
parameters to the URL. The Image component takes
the given src
and your sizes
values and creates a map of URLs, one for
each breakpoint in your theme. For example, if your theme has 5 breakpoints,
if you supply 5 values to sizes
, each value will map to one breakpoint. The
values can be either pixel values, or fractions of the viewport width.
For the example, image that your theme contains the following breakpoints:
[20rem, 40rem, 60rem, 100rem]
and you pass the following sizes array: [1, 1/2, 1/4, 200]
This indicates that for your smallest breakpoint (20rem
), you want to use
an image with a resolution that matches the viewport resolution (1
will be
converted to 100vw
, meaning 100% of the viewport). So, this will create
an image that is 320px wide (that is 20rem in pixels). It will also create an
image that is 640px wide, to account for 2x DPI displays.
Since 1/2
is given for the second value, that indicates that at the 40rem
breakpoint, you want an image that is half the size of the viewport. So, that
would create 2 images: (40 * 1/2) * 16)
and (40 * 1/2) * 16 * 2)
for the
2x DPI version. Again, that would create one 320px wide image and one 640px
image.
Hopefully you're getting an idea of how this works now! The next provided
value is 1/4
and the breakpoint is 60rem
. (60 * 1/4) * 16) === 240
. So
a 240px and 480px image would be generated for that breakpoint.
Finally, the last value in the sizes array is 200
. This is assumed to be a
px value (any value over 2 is assumed to be given in pixels). So for the
largest breakpoint, a 200px
and a 400px
image will be generated.
So, now we know exactly what size image to render at each breakpoint. This is passed to the srcSet, and the browser will handle loading the correct image based on the user's viewport and DPI.
For more info on how srcSet
works, check out
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset
Note
Once the browser has decided which image is the correct size for the current
viewport, we take that URL and set it as the background image of a div
, and
hide the actual img
. We do this so that it's easier to overlay the image
with text or other elements (by including them as children of the Image
component), and also to enable the cover
prop to employ background-size:
cover.
Setting the displayed width of your image
All of the above refers to the natural resolution of the image that will be loaded. To make the image responsive, it will be set to fill 100% of its container. If you want to restrict the width of the image on-screen, wrap it in a container and give that container a width.
For example, if you passed 1/2
for the 40rem
breakpoint, you may want to
wrap the image in a container to ensure it fills a maximum of 50%
of the
viewport at the 40rem
breakpoint (otherwise the image will look pixelated).
Usage
<Playground>
<Image
src="https://demostore.dev.jetshop.se/pub_images/original/224133.jpg"
sizes={[1 / 5, 1 / 2, 1]}
aspect="16:9"
alt="A bike"
/>
</Playground>
Aspect
The aspect
prop is used to define the aspect ratio of your image. You can
see this in action by expanding the demo above and changing the aspect
value.
By using the aspect
prop, your image will be resized to fit your desired
aspect ratio. By default your image will not be cropped, but you can opt-in
to cropping if you like.
Quality
By default, all images will be slightly compressed to optimise their
filesize. The Image component accepts a quality
prop, which may be set to a
number between 1-100. The default setting is 80.
Cropping
If you would like your image to be cropped to your specified aspect ratio,
you can pass the prop crop={true}
. This may be useful in cases where it's
not necessary to show the entire image (such as for banner images), and can
save data transfer by reducing the size of the displayed image.
Gravity
'north' | 'south' | 'west' | 'east' | 'centre' | 'smart'
When cropping an image, you may specify a 'gravity', which refers to the image region you want to crop to. north
will crop to the top of the image, west
to the left, and so on.
smart
will attempt to find the "most interesting" part of the image, and will crop in to that region.
<Playground>
<h2>Crop set to south</h2>
<Image
src="https://demostore.dev.jetshop.se/pub_images/original/224133.jpg"
sizes={[1 / 5, 1 / 2, 1]}
crop
gravity="south"
aspect="21:9"
alt="A bike"
/>
</Playground>
Fill available space
Sometimes you just want the image to fill the space available in the layout. This can be especially useful for banner images, for example.
To make this easier, you can apply the prop fillAvailableSpace={true}
. This
will force the image to fill its container by setting the height
to 100%
and setting the background-size
to cover
.
Try using fillAvailableSpace
along with crop
, aspect
, and gravity
to
optimise the size and positioning of your final image.
Note that you will need a container around your image in order to define the height.
<Playground>
<>
<small>
fillAvailableSpace, gravity="smart", crop — inside a wrapper with 100px
height
</small>
<div style={{ marginTop: "1em", height: 100 }}>
<Image
src="https://demostore.dev.jetshop.se/pub_images/original/224133.jpg"
sizes={[1 / 5, 1 / 2, 1]}
crop
fillAvailableSpace
gravity="smart"
alt="A bike"
/>
</div>
</>
</Playground>
Critical
By setting the critical
prop to true
, a LQIP will not be generated and the
image will instead load immediately. This is especially useful for hero images
and other images that are part of the "critical" page load - meaning those that
are displayed above-the-fold when a user first visits a page.
It's recommended that this is used on category images for example, as these are
often the largest image on the page, and loading them with critical
will
improve Google's Largest Contentful Paint
score (see https://web.dev/lcp/ for
more detail on LCP).
Styling images
For the most part, you should not need to style the Image component. If you
find yourself manually changing the height
, padding
, or
background-size
, you probably want to use the props described above
instead.
However, there are some data attributes applied to images that you can use in your CSS to style them if you need to.
[data-flight-image-root]
This is applied to all image components. You can target it in your CSS like this:
[data-flight-image-root] {
border: 1px solid pink;
}
[data-flight-image-loaded='true']
or [data-flight-image-loaded='false']
This will be false when the image is still loading or has an error, otherwise it will be true. You can target it in your CSS like this:
[data-flight-image-loaded="true"] {
border: 1px solid green;
}
[data-flight-image-loaded="false"] {
border: 1px solid red;
}
[data-flight-image-error='true']
or [data-flight-image-error='false']
This will be false when there is no error, and true when there is.
[data-flight-image-error="true"] {
border: 1px solid red;
}
[data-flight-image-error="false"] {
border: 1px solid green;
}
Broken and failed images
Broken/failed images will display an error message at the same aspect ratio and size as the image would have been, so the page layout will be maintained.
You can customise the error message using the error
prop.
Example with default error:
<Playground>
<Image src="http://example.com/broken-image.jpg" aspect="10:1" />
</Playground>
Example with custom error:
space that the original image would have been.
<Playground>
<Image
src="http://example.com/broken-image.jpg"
aspect="10:1"
error={() => (
<div style={{ background: "salmon", color: "pink", textAlign: "center" }}>
This image does not exist!
</div>
)}
/>
</Playground>