Loving Gatsby and the [gatsby-image] component!
Is there a way to use [gatsby-image] for background images as well? I frequently create page sections with a background image (background-size: cover
) and would love to take advantage of the auto-resizing and optimization features of [gatsby-image] for this use case.
(If [gatsby-image] can't be used for background images, can it somehow be used to simulate background-size: cover
?)
Great to hear it's working well for ya!
On background images, check out the gatsby-image demo site https://using-gatsby-image.gatsbyjs.org/
Is that what you want?
Thanks for the quick reply!
Just to make sure I understand what's happening on the gatsby-image demo site...
background-image
, gatsby-image always uses <img />
and background images are simulated using position: absolute;
?background-size/background-position
to position images, gatsby-image always uses object-fit/object-position
?I'm perfectly happy to use gatsby-image this way for all my images as long as they display properly in all modern browsers... Does Gatsby include measures to support the object-fit/object-position properties in IE 11 and Edge (see browser support issues here: caniuse.com)?
If so, would you recommend we just avoid CSS background images altogether (to take advantage of Gatsby's automated image processing)? Using gatsby-image, is there a recommended approach for adjust the object-fit/object-position properties?
If not, is there a recommended, cross-browser way to use background images with background-size:cover
in Gatsby?
@ooloth the component is very new so my answer to your question is… please play around with using gatsby-image as you'd like and report back what you find :-)
css background images are problematic because they don't allow you to (easily) add multiple thumbnail sizes for different sized devices. But you could work around this with media queries.
Haha. Understood. And will do. Thanks for your help!
If anyone else wants to share their best practices for the following, I'd be very interested:
object-fit/object-position
in IE 11 and Edge (does Gatsby already do this?)object-fit/object-position
values?Regarding 1., take a look at https://github.com/bfred-it/object-fit-images
@fk Thanks for the polyfill! As of Oct 16, Edge supports object-fit/object-position
, but IE11 still needs some help.
Can you please advise on one thing? I'm clear on how to install and import the polyfill...
npm install --save object-fit-images
import objectFitImages from 'object-fit-images'
..but I'm not sure where to place the activation call:
objectFitImages()
Can you please recommend where to place this line? I'm a bit new to both React and Gatsby.
(Btw, I've figured out how to adjust the object-fit/object-position values and will share that code as soon as I've sorted out adding the polyfill and its CSS adjustments.)
Alright, I've done some experimenting and have found a working solution for using gatsby-image for background images. I wanted to share this with anyone else getting stuck on this issue, as gatsby-image's features are too good to only use for inline images!
Here's what worked for me:
Before we add styles, it's good to know that the gatsby-image component outputs the following HTML:
<div class="gatsby-image-outer-wrapper">
<div class="gatsby-image-wrapper">
<div></div>
<!-- Placeholder (hidden after main image loads) -->
<img style="...object-fit: cover; object-position: center center;">
<!-- Main image -->
<img style="...object-fit: cover; object-position: center center;">
</div>
</div>
Styles set directly on gatsby-image will be applied to <div class="gatsby-image-wrapper>
. Styles we want to apply to the image itself need to use a child selector to target the <img>
tags.
To make gatsby-image act like a CSS background image, add the following styles (I've used gatsby-plugin-styled-components here):
import React from 'react'
import Image from 'gatsby-image'
import styled from 'styled-components'
const BgImage = styled(Image)`
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: -1;
height: 100vh; // or whatever
// Adjust image positioning (if image covers area with defined height) and add font-family for polyfill
& > img {
object-fit: cover !important; // or whatever
object-position: 0% 0% !important; // or whatever
font-family: 'object-fit: cover !important; object-position: 0% 0% !important;' // needed for IE9+ polyfill
}
`
export default BgImage
Here is a version of the same BgImage
component that uses props to set the dynamic values:
import React from 'react'
import Image from 'gatsby-image'
import styled from 'styled-components'
const BgImage = styled(Image)`
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: -1;
height: ${props => props.height || 'auto'};
// Adjust image positioning (if image covers area with defined height) and add font-family for polyfill
& > img {
object-fit: ${props => props.fit || 'cover'} !important;
object-position: ${props => props.position || '50% 50%'} !important;
font-family: 'object-fit: ${props => props.fit || 'cover'} !important; object-position: ${props => props.position || '50% 50%'} !important;'
}
`
export default BgImage
The use of !important
here is not ideal, but necessary to override the inline object-fit/object-position
styles.
Note that the object-fit
and object-position
properties will not need to be adjusted unless the gatsby-image covers an area with a set height. For these use cases, object-fit: cover;
usually still works for me and I just need to tweak the object-position
value (gatsby-image sets it to center center
by default).
The styles above will work in all browsers except IE (see caniuse.com: object-fit/object-position). Unfortunately, in IE, object-fit/object-position
do not work, and any background-image with a set height will stretch to fill its container, distorting the image.
Fortunately, there is a polyfill that fixes this in IE9+, which you can find here. (Thanks to @fk for pointing this out.)
Here's how to implement the polyfill:
font-family
declaration in your CSS (see above)npm install —save object-fit-images
gatsby-browser.js
file in the root of your projectgatsby-browser.js
:import objectFitImages from 'object-fit-images'
exports.onInitialClientRender = () => {
objectFitImages()
}
NOTE: I activated the polyfill in onInitialClientRender
because the polyfill's instructions are to place the activation call "before </body>
, or _on DOM ready_". Elsewhere, I've seen @KyleAMathews recommend that polyfills be implemented in onClientEntry
instead, so I'm not sure if that would be better (any comment, Kyle?).
Hope that helps!
Argh damn, sorry for not coming back earlier @ooloth! Glad you figured it out!
Thank you for coming back and writing up your findings! 👍 ❤️
My pleasure!
@stolinski runs into the same issue in one of his tutorials. He goes about it a different way:
https://www.youtube.com/watch?v=zhM6C0P7VO0
<Img
sizes={dataSizes}
style={{
position: "absolute",
left: 0,
top: 0,
width: "100%",
height: "100%"
}}
/>
Much simpler! Thanks @grod220.
@grod220 that was easy... Thank you 😄
@enten That would work, but you'd miss out on the performance advantages of using gatsby-image
to deliver an appropriately-resized version of the image at each viewport size via the srcset
and sizes
attributes.
For me, the ability to use gatsby-image
to easily deliver the optimal copy of each image is Gatsby's best feature. Those benefits aren't available when you use CSS background images.
@ooloth Thanks for covering this. Gatsby newbie here. In your instructions with the styling, where does the actual image mentioned?
@fucata55 My pleasure!
The actual image is something you first query via graphql and then pass as a prop to your gatsby-image
component's fluid
or fixed
prop (or if using v1, its sizes
or resolutions
prop).
The gatsby-image
component will then generate all the copies of the image you'll need as well and add the complicated sizes
attribute to the <img />
in the resulting HTML for you.
For a walkthrough on how to use gatsby-image
(and send an image to it), see the gatsby-image
docs here for v2 or here for v1 (they're great).
@ooloth your work has been very helpful. Thank you.
Is it possible to use "background-attachment: fixed; " ? If so, where exactly. I can get it working but not with safari. When I get it working the behavior is that the image looks good until I add more components in the page. The more components added the bigger the picture gets. So, then I polyfill for the safari issue but then the "background-attachment: fixed; " is no longer an option, or so it seems. Any help greatly appreciated.
@zachgaskin I wish I could help!
This sounds like it's probably a CSS issue (i.e. not related to Gatsby or gatsby-image
) and I'm afraid I don't have much experience using background-attachment: fixed
and haven't encountered the issue you're describing. If you're able to post a link to a minimal reproduction of this issue, I'd be happy to take a look, though.
According to caniuse.com, iOS Safari doesn't support background-attachment: fixed
, but macOS Safari should work (should...).
It can be a real pain to sort out why certain browsers treat the same CSS differently, so you have my sympathy. Good luck!
@zachgaskin @ooloth I've come across this too. It's not ideal but the solution I have come up with is adding position: 'fixed'
to the image. Although, this means it is in the background of the whole page. You need to make sure you have a background specified everywhere else. The z-index: -1
will make sure it is hidden behind your other backgrounds.
<Img
sizes={dataSizes}
style={{
position: "fixed",
left: 0,
top: 0,
width: "100%",
height: "100%",
z-index: -1
}}
/>
Gatsby Background Image is a step forward...
You might find useful to import gatsby-image directly with a IE polyfill and apply objectFit and objectPosition to it as props, instead of using !important in your styles
import Img from 'gatsby-image/withIEPolyfill'
<Img
fixed={heroImage.childImageSharp.fixed}
style={{ width: '100%', height: '100%' }}
objectFit="cover"
objectPosition="bottom left"
/>
Thanks, @AronBe
I'm not working with Gatsby at the moment, so I don't know the latest; however, Gatsby Background Image (mentioned above) really did the trick for me on my last project.
Most helpful comment
Alright, I've done some experimenting and have found a working solution for using gatsby-image for background images. I wanted to share this with anyone else getting stuck on this issue, as gatsby-image's features are too good to only use for inline images!
Here's what worked for me:
1. Add CSS Styling
Before we add styles, it's good to know that the gatsby-image component outputs the following HTML:
Styles set directly on gatsby-image will be applied to
<div class="gatsby-image-wrapper>
. Styles we want to apply to the image itself need to use a child selector to target the<img>
tags.To make gatsby-image act like a CSS background image, add the following styles (I've used gatsby-plugin-styled-components here):
Here is a version of the same
BgImage
component that uses props to set the dynamic values:The use of
!important
here is not ideal, but necessary to override the inlineobject-fit/object-position
styles.Note that the
object-fit
andobject-position
properties will not need to be adjusted unless the gatsby-image covers an area with a set height. For these use cases,object-fit: cover;
usually still works for me and I just need to tweak theobject-position
value (gatsby-image sets it tocenter center
by default).2. Add IE9+ Polyfill for object-fit/object-position
The styles above will work in all browsers except IE (see caniuse.com: object-fit/object-position). Unfortunately, in IE,
object-fit/object-position
do not work, and any background-image with a set height will stretch to fill its container, distorting the image.Fortunately, there is a polyfill that fixes this in IE9+, which you can find here. (Thanks to @fk for pointing this out.)
Here's how to implement the polyfill:
font-family
declaration in your CSS (see above)npm install —save object-fit-images
gatsby-browser.js
file in the root of your projectgatsby-browser.js
:NOTE: I activated the polyfill in
onInitialClientRender
because the polyfill's instructions are to place the activation call "before</body>
, or _on DOM ready_". Elsewhere, I've seen @KyleAMathews recommend that polyfills be implemented inonClientEntry
instead, so I'm not sure if that would be better (any comment, Kyle?).Hope that helps!