Best practices around images have been evolving over the past couple of years, bringing things like new image formats (AVIF!) and improved browser support for APIs. We of course want to make sure that Gatsby users are able to take advantage of all of these advancements, and we also want to make working with images and Gatsby as frictionless as possible.
Which is why Gatsby v3.0, our first major version release in two years, included an all-new gatsby-plugin-image
. We’ve doubled down on one of the biggest strengths of the Gatsby framework — robust, optimized image handling — and made it even better, even easier to use, and more performant.
Our new next-gen Gatsby image plugin is designed to make the job of including high-performance, responsive images in your site as smooth as possible. However, there are still some optimizations to help you achieve peak results — and avoid a few common pitfalls. Follow these tips and your images will be super-speedy and your Lighthouse scores will always be green.
Choose the right layout
The new plugin includes three different types of layout, and for best results you should make sure you’re choosing the right one. This ensures that you don’t generate more images than needed and that browsers can download the best available size.
- Constrained layout. This is the default layout, and works best for most cases. The image will never be displayed larger than the source image, or width and height that you specify, but will scale down to fit smaller displays.
- Fixed layout. This behaves like a regular
<img>
tag: it displays the image at the requested size, and never scales it up or down. - Full-width layout. This is for images that should always take up the full width of their container, even if it is larger than the source image. Typically full-width layout is only used for hero images or banners that span the full width of the screen.
Set default values for gatsby-plugin-sharp
Do you always want to generate AVIF images? Good choice!
Instead of always passing formats=[AUTO, WEBP, AVIF]
in your GraphQL queries or StaticImage props, you can set defaults in gatsby-plugin-sharp
in gatsby-config-js
. These will then be applied for every gatsbyStaticImage
or childImageSharp
query. You can set defaults for most processing options, including image formats; quality levels for each format, breakpoints; and also default placeholder type. For details, see the gatsby-plugin-sharp docs.
Note: Bear in mind that this only applies to images processed using sharp, not ones that are delivered directly from a CMS or image CDN.
You probably don’t need to set breakpoints
It can be tempting to pass your own set of breakpoints to the image resolver to set the images to generate, but in almost all cases this is not needed.
For constrained and fixed images, the breakpoints are generated based on the size of the displayed image. If the source image is large enough, it will generate larger images to support high-density displays, and for constrained layouts it will also generate smaller images for smaller displays.
For full-width images it generates the following widths (so long as the source image is large enough): 750
, 1080
, 1366
, 1920
. These widths have been chosen based on the latest global device usage analytics and offer a good spread of support for devices in 2021. We will keep this list of sizes up to date in future releases to track with changes in device usage, so if you stick to the defaults then you never need to worry about changes to the breakpoint sizes.
You may be tempted to add smaller sizes to support mobile devices. However this is not needed! While there are plenty of devices in use with displays narrower than 750px, essentially none of these have 1x pixel density displays — meaning their browser will always request a larger image if it is available. 750px is a good minimum size, supporting both 2x density phones and also 1x density tablets.
The main scenario where you might want to generate extra breakpoints is if you want to generate extremely large images to support full density images, e.g. on 5K monitors. Bear in mind that this can add significantly to both build and deploy times and also download sizes for users.
Use loading=eager
(but not too much)
By default, the Image component uses lazy loading to only load images that are visible on screen. This is the best choice in most cases. However, if you pass loading="eager"
to the Image component, the browser will start loading it immediately – even before React hydration happens.
This is useful for images that are vital to display immediately, but should only be used for images above the fold. If you use it for images that are offscreen, the browser will waste resources downloading and rendering these images when it could be rendering things that the visitor can see.
Best practice: Restrict loading="eager"
to a few important images above the fold.
Consider using sizes if images won’t span the full screen
Gatsby automatically generates multiple resolutions of an image, depending on your chosen layout and size, and allows the browser to decide which to download. You can pass a sizes prop to help the browser choose the optimal resolution. This is not required, but it can help get the best performance. It does not affect which sources are generated, but does allow you to provide the browser with a hint about which it should use.
By default, we generate a value for sizes
that works in most cases. If, however, you are displaying a full width or constrained image inside a container that will never span the full width of the screen, you can let the browser know this. The values passed to sizes
are a media query that describes the width of the image displayed on screen.
This example for an 800px-wide image shows the default values and how you could adjust them:
- Fixed layout: Default value for an 800px-wide image is
sizes="800px"
. Since the image never resizes, the value is the fixed width of the image. You should never need to adjust this. - Full-width layout: Default value
sizes="100vw"
. A full-width image is designed to span the full width of the screen, so thesizes
value is100vw
, i.e. the full width of the viewscreen.
If you need to have a full width layout that has 30px padding on either side, you can change the value to match, e.g.sizes="calc(100vw - 60px)"
.
If an image is half the width of the screen but you still need it to expand indefinitely, you could uses a sizes value like this:sizes="50vw"
.
Remember: if you constrain the maximum width at all, you should use the constrained layout rather than full-width.
- Constrained layout: The value for this layout is a little more complicated, as there are two rules to define how wide the image is:
- If the container is wider than the image, then the image is displayed at a fixed width.
- If the container is narrower than the image, it is displayed at the screen width.
The default value assumes that the container can span the full width of the screen when the window is smaller than the image. A sizes prop is evaluated from left to right until it finds a media query that matches.
For the example of an 800px-wide image, the generated value is: sizes="(min-width: 800px) 800px, 100vw"
. This is two rules, separated by a comma. The first rule has a media query that matches when the screen is at least 800 pixels wide. In that case, the image is 800px wide. The second rule is matched otherwise, in which case the image is displayed at the full width of the screen (i.e. 100vw).
Otherwise, if you have the constrained image in a container that does not span the full width of the screen then you can adjust the values to match. Imagine a layout with two constrained images side-by side:
You would then need a rule that says that the image will be half the width of the screen, unless the screen is wider than 1600px:
If your rules are even more complicated, you can add even more rules to this list. They are evaluated from left to right. However, you don’t need to sweat this too much: browsers are generally good at deciding which to download, even without a sizes prop. We generate pixel-width srcsets
, rather than pixel density, as this gives the most flexibility to the browser.
Try loading your page on several different devices (the browser dev tools can emulate different resolutions and network types) and observe which images it is downloading.
You probably don’t need to worry about it!
Despite all of these tips, in most cases you will be absolutely fine using the default values. We have put a lot of work into ensuring that the Gatsby image plugin and components deliver great performance and best practices out of the box, with sensible defaults that take into account how you are using the images.
Take this minimal example:
This will download the source image, determine its dimensions and ensure that the component is displayed at the correct size. It will calculate the dominant color of the image and use that as the background color of the placeholder. It will generate WebP and JPG formats in multiple resolutions based on the image size, and it will display it all in a responsive container that resizes with the browser window. It will then display these all with a <picture> element that lazy-loads in all modern browsers, using native lazy loading if available, or IntersectionObserver otherwise. It even automatically polyfills object-fit in IE11 if needed.
In other words, Gatsby Image will in most cases by default give you 💯s on Lighthouse and a great experience for all of your users.
Register for our new Gatsby Image webinar next week, on Wednesday April 7th (8am PT | 11am ET | 3pm UTC), when Gatsby Software Engineers Matt Kane and Obinna Ekwuno will do a live demonstration of the all-new Gatsby Image plugin, identify a few common user pitfalls we see (and how to avoid them), and do live Q&A.
Can’t join us Wednesday? No worries, go ahead and register for the webinar, and we’ll send you a captioned recording within a day or two after the event.