Scaling / Resizing Responsive Retina CSS Sprite Icon in React

Published on 3 years agoScaling / Resizing Responsive Retina CSS Sprite Icon in React

Images are crucial elements that will bring a strong visual power to a web page. Too many images, however, will deteriorate the performance of a web page due to too many requests to the server.

One of the optimisation technique we can use is CSS Sprite, which basically combining multiple images into one image. Then, you could use CSS background-position to shift around and only display part of it.

Pros of CSS Sprite

  • Reduce the requests to server and allow more parallelism request for other resources.
  • Reduce overhead of handshake and header for each HTTP requests.

Cons of CSS Sprite

  • Too large image files may slow down First Meaningful Paint metric and negatively impact other metrics as well. Always benchmark and measure!
  • May have some overhead to regenerate the image sprites every time you add a new image.

Creating CSS Sprite Icon

Today, we are going to try to create an Icon component using CSS sprite in React and see how it works. You can clone the starter project and follow the exercise.

Creating CSS Sprite

There are many ways to generate a CSS sprite:

  • Using cloud tools such as Toptal Sprite Generator and etc.
  • Using image tool such as ImageMagick, Adobe Illustrator and etc.
  • Using Grunt / Gulp / Node tools such as sprity.

For this post, we are not going to look into how to generate the sprite, this process will have integrate into your development flow such as your CI/CD flow. For this tutorial, we already have some generated image sprites that is ready to use in the public/images which contains some icons of social network:

  • icon.webp
  • icon@2x.webp
  • icon@3x.webp.

These icons are generate from image get from flaticon.com.

Let's get started by adding some classes in App.css:

// Other CSS

+.icon {
+  background: url(/images/icon.webp) no-repeat top left;
+  width: 32px;
+  height: 32px;
+}
+
+@media only screen and (-webkit-min-device-pixel-ratio: 2) {
+  .icon {
+    background: url(/images/icon@2x.webp) no-repeat top left;
+    background-size: 32px 128px;
+  }
+}
+
+@media only screen and (-webkit-min-device-pixel-ratio: 3) {
+  .icon {
+    background: url(/images/icon@3x.webp) no-repeat top left;
+    background-size: 32px 128px;
+  }
+}

We added icon class and set the background to the image with width and height of 32px. Also, to make it display nicely in Retina display devices, we also added 2x and 3x images using -webkit-min-device-pixel-ratio media queries.

Next, we will create an Icon component to display the image. Let's create a src/Icon.tsx:

import { useMemo } from "react";

export type IconType = 'facebook' | 'instagram' | 'twitter' | 'whatsapp';

export interface IconProps {
  iconName: IconType;
  size?: number;
}

const iconIndex: Record = {
  facebook: 0,
  instagram: 1,
  twitter: 2,
  whatsapp: 3,
}
const iconSize = 32;

export const Icon = ({ iconName, size = iconSize }: IconProps) => {
  const positionStyle = useMemo(() => {
    return {
      backgroundPosition: `-0px -${iconIndex[iconName] * iconSize}px`,
      transform: `scale(${size / iconSize})`,
    }
  }, [iconName, size]);

  return (
    <div className="icon" style={positionStyle} />
  )
}

By using iconIndex and iconSize, we can easily calculate the position of each image that we want to display. We will pass the iconName as the props to the Icon component and reuse the same component anywhere we want to.

In order to scale the icon, we have added a size props to allow us to change the size of the icon by calculating the scale ratio using the desire size against the actual icon size.

Lastly, let's add some code to test our Icon component in App.tsx:

import './App.css';
+import { Icon } from './Icon';

function App() {
  return (
    <div className='gallery'>
+      <Icon iconName="facebook" size={16} />
+      <Icon iconName="instagram" size={24} />
+      <Icon iconName="twitter" size={32} />
+      <Icon iconName="whatsapp" size={40} />
    </div>
  );
}

export default App;

Great! We have successfully added 4 Icons in the page with different size and iconName and let's run yarn start to see the result.

Now we should be able to see the 4 icons is render properly with different sizes.

Conclusion

CSS Sprite is one of the way to optimise your image assets as well as web loading speed.

There are many other image optimisation techniques exist in the industry such as lazy loading the image, using embedded SVG and etc to further allow you to put more images in your webpage without compromise the performance.

Here is the complete code for this tutorial if you interested.

Feel free to leave the comment below.

Copyright © 2024 Tek Min Ewe