CleverImage Astro Component for My Responsive Images

Create responsive images in pure HTML with the CleverImage component. Let me show you how easy it is. The full code written in this post can be found at GitHub Gist: CleverImg.astro.

What is a responsive image?

Responsive images work well o…


This content originally appeared on DEV Community and was authored by Tömő Viktor

Create responsive images in pure HTML with the CleverImage component. Let me show you how easy it is. The full code written in this post can be found at GitHub Gist: CleverImg.astro.

What is a responsive image?

Responsive images work well on all kinds of screen sizes and resolutions. It can help in optimizing page performance. For example, you don't need a 4k image on a mobile screen because it is just a waste.

It can be done in pure HTML. To create one, you have to modify an img element's attributes:

  • srcset: specify multiple image sources and their scale factors (note that if sizes isn't defined then the browser will decide which to display, that is why order matters here, the browser will use the first usable image it finds)
  • sizes: specify which image source to use from the srcset on which screen sizes.

These two attributes value generation will be implemented.

Example

In the previous part we created an automatic image generator, look at that for generating images, I am going to use an example related to that.

Let's say our base image is cat.png, it has different sizes and formats (file names scheme: [NAME]-[WIDTH]-[HEIGHT].[EXTENSION]): cat-100-500.png, cat-100-500.webp, cat-200-1000.png, cat-200-1000.webp. The smaller image should be loaded if the user's screen is smaller than 768px. WebP is preferred.

<img 
  src="cat-200-1000.png"
  alt="a black cat with big green eyes"
  srcset="cat-100-500.webp 100w,
          cat-100-500.png 100w,
          cat-200-1000.webp 200w,
          cat-200-1000.png 200w"
  sizes="(max-width: 768px) 100px, 200px"
/>

For more explanation read MDN - Responsive images.

Implementation

Setup

Create a new Astro component, let's name it CleverImg.astro.

What props are needed?

  • Basic img tag arguments: imgPath (src), alt, loading (if later want to add lazy load)
  • sizes what tells the sizes to generate for srcset
  • breakpoints that declares at what maxWidth which size source should be loaded, here 0 maxWidth will be the default non media query related size
  • withWebp if the image(s) exist in .webp format too
interface Props {
  imgPath: string,
  alt: string,
  loading?: "eager" | "lazy" | null,
  sizes: { width: number; height: number }[],
  breakpoints?: {maxWidth: number, imgWidth: number}[]
  withWebp?: boolean,
}
const { imgPath, alt, sizes, withWebp, loading, breakpoints } = Astro.props; // init each prop as a const variable

Both srcset and sizes require separating strings with a comma. That is why both will be handled as an array and will be joined after.

const generatedSrcset: string[] = [];
const generatedSizes: string[] = [];

If you followed the previous part, and images are only generated on build, then put the following code into an if, so it only runs on build:

if (import.meta.env.PROD) {
  // ...
}

Generate srcset

First, sort the sizes. Because order matters, order them ascending.

sizes.sort((a, b) => a.width - b.width);

Now, we have to append strings to generatedSrcset in format [IMGPATH] [IMGWIDTH]w. Path will be generated with function that was written in previous part of this series. This must be done for WebP format too.

for (const size of sizes) {
  ((withWebp ?? true) ? [true, false] : [false]).forEach((isWebp) => { // to do it for webp too
    generatedSrcset.push(`${ImageGenerator.generateName(imgPath, size, isWebp)} ${size.width}w`);
  });
}

There is a small modification which should be made. If there is a breakpoint, only include images which are in that. Because breakpoint will tell the browser to display which image and when, just those images are required. For this, let's filter the sizes and remove ones that aren't required.

let allBreakpointSizes: number[] | null = null;
if (breakpoints) {
  allBreakpointSizes = breakpoints.map(bp => bp.imgWidth);
}
sizes.sort((a, b) => a.width - b.width);
for (const size of sizes) {
  if (allBreakpointSizes && allBreakpointSizes.indexOf(size.width) === -1) {continue;} // skip if the curren width is not found in breakpoints
  ((withWebp ?? true) ? [false, true] : [false]).forEach((isWebp) => {
    generatedSrcset.push(`${ImageGenerator.generateName(imgPath, size, isWebp)} ${size.width}w`);
  });
}

Generate sizes

Add strings to generatedSizes in (max-width: [MAXWIDTH]px) {IMGWIDTH}px format. There is one exception, if the maxWidth is 0 then it means that this is the default value.

if (breakpoints) {
  for (const breakpoint of breakpoints) {
    if (breakpoint.maxWidth === 0) {
      generatedSizes.push(`${breakpoint.imgWidth}px`); // handle default
    } else {
      generatedSizes.push(`(max-width: ${breakpoint.maxWidth}px) ${breakpoint.imgWidth}px`);
    }
  }
}

Use it in HTML

The src should be the biggest sized image. If someone's browser doesn't support srcset then this will be used. The other attributes are nothing special, the generated arrays must be joined with comma.

---
// ...
const biggestSize = sizes.reduce((prev, current) => {
  return (prev.width > current.width) ? prev : current;
});
---
<img
  src={ImageCompressorIntegration.generateName(imgPath, biggestSize)}
  alt={alt}
  srcset={generatedSrcset.join(", ")}
  sizes={generatedSizes.join(", ")}
  loading={loading}
/>

If you followed the previous part, and images are only generated on build, then put the fully generated img in a conditional, and put a simple img in with import.meta.env.DEV condition. Look at the implementation at GitHub Gist: CleverImg.astro.


This content originally appeared on DEV Community and was authored by Tömő Viktor


Print Share Comment Cite Upload Translate Updates
APA

Tömő Viktor | Sciencx (2023-05-12T16:02:14+00:00) CleverImage Astro Component for My Responsive Images. Retrieved from https://www.scien.cx/2023/05/12/cleverimage-astro-component-for-my-responsive-images/

MLA
" » CleverImage Astro Component for My Responsive Images." Tömő Viktor | Sciencx - Friday May 12, 2023, https://www.scien.cx/2023/05/12/cleverimage-astro-component-for-my-responsive-images/
HARVARD
Tömő Viktor | Sciencx Friday May 12, 2023 » CleverImage Astro Component for My Responsive Images., viewed ,<https://www.scien.cx/2023/05/12/cleverimage-astro-component-for-my-responsive-images/>
VANCOUVER
Tömő Viktor | Sciencx - » CleverImage Astro Component for My Responsive Images. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/05/12/cleverimage-astro-component-for-my-responsive-images/
CHICAGO
" » CleverImage Astro Component for My Responsive Images." Tömő Viktor | Sciencx - Accessed . https://www.scien.cx/2023/05/12/cleverimage-astro-component-for-my-responsive-images/
IEEE
" » CleverImage Astro Component for My Responsive Images." Tömő Viktor | Sciencx [Online]. Available: https://www.scien.cx/2023/05/12/cleverimage-astro-component-for-my-responsive-images/. [Accessed: ]
rf:citation
» CleverImage Astro Component for My Responsive Images | Tömő Viktor | Sciencx | https://www.scien.cx/2023/05/12/cleverimage-astro-component-for-my-responsive-images/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.