Last active
July 18, 2022 16:16
-
-
Save maltesa/7a866131ec824180daf536f38dc849e1 to your computer and use it in GitHub Desktop.
Custom Image component using images.weserv.nl
This file contains hidden or 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
| /* eslint-disable jsx-a11y/alt-text */ | |
| /* eslint-disable @next/next/no-img-element */ | |
| import type { DetailedHTMLProps, ImgHTMLAttributes } from 'react' | |
| interface OptimizationOptions { | |
| width: number | |
| height?: number | |
| dpr?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
| format?: 'png' | 'jpg' | 'webp' | |
| } | |
| function optimized(src: string, { width, height, dpr, format }: OptimizationOptions) { | |
| const encodedSrc = encodeURIComponent(src) | |
| let url = `https://images.weserv.nl/?url=${encodedSrc}&w=${width}` | |
| if (height) url = `${url}&h=${height}&fit=cover&a=attention` | |
| if (dpr) url = `${url}&dpr=${dpr}` | |
| if (format) url = `${url}&output=${format}` | |
| return url | |
| } | |
| type HTMLImagePropsWithoutSrc = Omit< | |
| DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>, | |
| 'src' | |
| > | |
| interface Props extends HTMLImagePropsWithoutSrc { | |
| src: string | |
| width?: number // Should be the maximum width the image is displayed at | |
| height?: number | |
| responsive?: boolean | |
| } | |
| export function CustomImage({ | |
| src, | |
| width = 1280, | |
| height, | |
| responsive = true, | |
| loading = 'lazy', | |
| ...imgProps | |
| }: Props) { | |
| const isPng = src.endsWith('.png') | |
| const fallbackFormat = isPng ? 'png' : 'jpg' | |
| const fallbackSrc = optimized(src, { width, height, format: fallbackFormat }) | |
| return ( | |
| <picture> | |
| <PrimarySources src={src} width={width} height={height} responsive={responsive} /> | |
| <source srcSet={fallbackSrc} type={fallbackFormat} /> | |
| <img src={fallbackSrc} width={width} height={height} loading={loading} {...imgProps} /> | |
| </picture> | |
| ) | |
| } | |
| interface PrimarySourcesProps { | |
| src: string | |
| width: number | |
| height?: number | |
| responsive: boolean | |
| } | |
| function PrimarySources({ src, width, height, responsive }: PrimarySourcesProps) { | |
| if (src.endsWith('.svg')) { | |
| return <source srcSet={src} type="image/svg+xml" /> | |
| } else if (!responsive) { | |
| const webpUrl = optimized(src, { width, height, format: 'webp' }) | |
| return <source srcSet={`${webpUrl}`} type="image/webp" /> | |
| } else { | |
| const widthSm = Math.round(width / 2) | |
| const heightSm = height && Math.round(height / 2) | |
| const widthMd = Math.round(width / 1.5) | |
| const heightMd = height && Math.round(height / 1.5) | |
| const webpUrlSm = optimized(src, { width: widthSm, height: heightSm, format: 'webp' }) | |
| const webpUrlMd = optimized(src, { width: widthMd, height: heightMd, format: 'webp' }) | |
| const webpUrlXl = optimized(src, { width, height, format: 'webp' }) | |
| const webpUrlSm2x = optimized(src, { width: widthSm, height: heightSm, format: 'webp', dpr: 2 }) | |
| const webpUrlMd2x = optimized(src, { width: widthMd, height: heightMd, format: 'webp', dpr: 2 }) | |
| const webpUrlXl2x = optimized(src, { width, height, format: 'webp', dpr: 2 }) | |
| return ( | |
| <> | |
| <source | |
| media="(max-width: 640px)" | |
| srcSet={`${webpUrlSm} 1x, ${webpUrlSm2x} 2x`} | |
| type="image/webp" | |
| /> | |
| <source | |
| media="(max-width: 768px)" | |
| srcSet={`${webpUrlMd} 1x, ${webpUrlMd2x} 2x`} | |
| type="image/webp" | |
| /> | |
| <source srcSet={`${webpUrlXl} 1x, ${webpUrlXl2x} 2x`} type="image/webp" /> | |
| </> | |
| ) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment