Skip to content

Instantly share code, notes, and snippets.

@nicovalencia
Last active April 2, 2021 19:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicovalencia/873aa626f91054c076f53fde38ebf5d6 to your computer and use it in GitHub Desktop.
Save nicovalencia/873aa626f91054c076f53fde38ebf5d6 to your computer and use it in GitHub Desktop.
Fluid Width Responsive Next.js Image
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
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
@nicovalencia
Copy link
Author

nicovalencia commented Apr 2, 2021

Demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment