Last active
April 2, 2021 19:16
-
-
Save nicovalencia/873aa626f91054c076f53fde38ebf5d6 to your computer and use it in GitHub Desktop.
Fluid Width Responsive Next.js Image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PropTypes from 'prop-types' | |
import styled from 'styled-components' | |
import ResponsiveImage from './ResponsiveImage' | |
import theme from '../../util/theme' | |
const MaskContainer = styled.div` | |
overflow: hidden; | |
display: flex; | |
justify-content: center; | |
` | |
const Wrapper = styled.div` | |
${props => | |
Object.keys(props.theme.bp).map(bp => | |
props.theme.bp[bp](` | |
width: ${props.sizes[bp][0]}px; | |
height: ${props.sizes[bp][1]}px; | |
`), | |
)}; | |
` | |
/** | |
* <FluidWidthResponsiveImage /> | |
* Loads a responsive optimized image into a fixed height wrapper. | |
* | |
* The wrapped image has *responsive* height and *fluid* width. | |
* | |
* - The correct responsive asset is loaded with Next.js. | |
* - The MaskContainer centers the wrapped image and hides the excess image. | |
* - The Image Wrapper sets the correct height/width for the responsive asset. | |
* - The ResponsiveImage loads the correct asset sizes using Next.js Image. | |
* | |
* fixedHeights - set of fixed heights per-breakpoint {xs: 200, ...} | |
* aspectRatio - full size asset (width/height) | |
*/ | |
const FluidWidthResponsiveImage = ({ fixedHeights, aspectRatio, ...rest }) => { | |
// Calculate asset sizes based on provided fixedHeights and aspect ratio: | |
const sizes = {} | |
Object.keys(theme.bp).forEach(bp => { | |
let width = Math.round(aspectRatio * fixedHeights[bp]) | |
let height = fixedHeights[bp] | |
sizes[bp] = [width, height] | |
}) | |
return ( | |
<MaskContainer> | |
<Wrapper sizes={sizes}> | |
<ResponsiveImage sizes={sizes} {...rest} /> | |
</Wrapper> | |
</MaskContainer> | |
) | |
} | |
const fixedHeightsPropTypes = {} | |
Object.keys(theme.bp).forEach(bp => { | |
fixedHeightsPropTypes[bp] = PropTypes.number.isRequired | |
}) | |
FluidWidthResponsiveImage.propTypes = { | |
fixedHeights: PropTypes.shape(fixedHeightsPropTypes).isRequired, | |
aspectRatio: PropTypes.number.isRequired, | |
} | |
export default FluidWidthResponsiveImage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PropTypes from 'prop-types' | |
import styled from 'styled-components' | |
import Image from 'next/image' | |
import theme from '../../util/theme' | |
const Wrapper = styled.div` | |
position: relative; | |
${props => | |
Object.keys(props.theme.bp).map(bp => | |
props.theme.bp[bp](` | |
width: ${props.sizes[bp][0]}px; | |
height: ${props.sizes[bp][1]}px; | |
`), | |
)}; | |
` | |
const ResponsiveImage = ({ sizes, ...rest }) => ( | |
<Wrapper sizes={sizes}> | |
<Image {...rest} layout="fill" /> | |
</Wrapper> | |
) | |
const sizesPropTypes = {} | |
Object.keys(theme.bp).forEach(bp => { | |
sizesPropTypes[bp] = PropTypes.number.isRequired | |
}) | |
ResponsiveImage.propTypes = { | |
sizes: PropTypes.shape(sizesPropTypes).isRequired, | |
} | |
export default ResponsiveImage |
Author
nicovalencia
commented
Apr 2, 2021
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment