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.
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.
There are many ways to generate a CSS sprite:
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.
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