Progressive and Effective Image Loading
--
Optimizing image assets is crucial for improving performance and delivering a seamless user experience in web and mobile applications. Large image sizes can significantly impact loading times, resulting in slower page rendering and frustrated users. In this article, we will talk an approach we implemented in Trendyol. That approach is about enhance frontend performance by implementing both image compression and size limitations while incorporating progressive image loading techniques. Specifically, we will focus on a scenario where uploaded images are limited to 4MB and automatically compressed to a maximum width or height of 1800 pixels. Additionally, we will discuss how to implement progressive image loading in React Native, prioritizing the loading of lower-resolution images before the actual high-resolution images.
Prior to this development, our main problem was that we were compressing user uploaded images and providing lower resolution images. Then, we increased the size, width and height limit in images and offered users the ability to upload higher resolution images. But this brought other problems as well. Now, users started to view the images they uploaded more slowly. To solve this, we used progressive image upload method.
Understanding Image Compression and Size Limitations
Images are a fundamental part of our web and mobile applications, enriching user interfaces with visual content. However, large image file sizes can significantly impact performance, resulting in longer loading times and increased bandwidth consumption. Image compression and size limitations are essential techniques that address these challenges, enabling frontend developers to strike a balance between image quality and file size.
Regarding this, we ran into a problem because our compression ratio was too high and limitation was too low, the images were not looking good. Therefore, we applied a better compression method with higher limits such as 4MB and 1800 pixel limit. We restrict the upload of 4MB images, then compress images smaller than 4MB. Then, if the height and width of the pictures are higher than 1800px, we preserve the aspect ratio according to the height and width and reduce it to 1800px.
- Image Compression: Image compression is the process of reducing the file size of an image without significantly degrading its visual quality.
With the code below we compress images to upload more efficiently.
const compressed = await sharp(file.buffer)
.flatten({ background: { r: 255, g: 255, b: 255, alpha: 0 } })
.jpeg({ quality: 80 })
.toBuffer();
- Setting Size Limitations: Setting size limitations for uploaded images helps control the maximum allowed file size and dimensions, preventing the upload of excessively large images. By specifying limitations, developers can enforce constraints that ensure a more efficient use of server storage space and bandwidth. In this scenario, limiting images to 4MB and a maximum width or height of 1800 pixels allows for a reasonable compromise between quality and performance.
With the code below we reduced the width and height of the images if they were more than 1800 pixels but kept the aspect ratio as well.
const { width, height } = fileDimension;
const WIDTH_LIMIT = 1800;
const HEIGHT_LIMIT = 1800;
if ((width > WIDTH_LIMIT || height > HEIGHT_LIMIT)) {
const ratio =
width > height
? width / height
: height / width;
const newWidth =
width > WIDTH_LIMIT && width > height
? WIDTH_LIMIT
: WIDTH_LIMIT / ratio;
const newHeight =
height > HEIGHT_LIMIT && height > width
? HEIGHT_LIMIT
: HEIGHT_LIMIT / ratio;
const newDimension = { width: newWidth, height: newHeight };
}
By doing this, we converted an image of 3.2 MB in size, 4794x3200 in width and height, into an image of 143 KB in size, 1800x1201 in width and height, with an acceptable resolution drop.
Progressive Image Loading
Progressive image loading is an approach that prioritizes the rendering of lower-resolution or placeholder images before gradually replacing them with higher-resolution versions. This technique aims to improve the perceived loading time for users, providing a more engaging and seamless visual experience. By implementing progressive image loading, frontend developers can enhance user satisfaction and mitigate the negative impact of slow network connections or large image sizes.
Hence, if you are trying to load images with higher resolution and you have slow internet connection, you may wait longer in order to see images. We had this problem with images we created with a higher size limit and a lower compression ratio. That’s why we implemented the progressive image loading method.
Benefits of Progressive Image Loading
- Improved Perceived Performance: By initially displaying lower-resolution or compressed images, progressive loading gives the illusion of faster loading times. This enhances user satisfaction and provides a better overall experience, especially when dealing with large or slow-loading images.
- Bandwidth Optimization: Progressive image loading reduces the amount of data that needs to be transferred to the client device. By prioritizing the display of lower-resolution images, less bandwidth is consumed during the initial loading phase, making it more efficient, especially in low-bandwidth scenarios.
- Better User Engagement: Users are more likely to engage with content that loads quickly and smoothly. By reducing the perceived loading time, progressive image loading improves the user experience and encourages users to interact with images and other visual elements on the webpage or mobile app.
- Adaptive to Varying Network Conditions: Progressive image loading adapts well to different network conditions, such as slow connections or intermittent disruptions. It provides a better experience for users in areas with limited internet access or unstable connections, ensuring that images are displayed progressively regardless of the network quality.
Standard Image Loading vs Progressive Image Loading
When it comes to loading images on the web or in mobile applications, there are two primary approaches: standard image loading and progressive image loading. Let’s explore the differences between these two methods and examine their advantages and disadvantages.
Standard Image Loading: Standard image loading follows a traditional approach where the full image is loaded before it is displayed to the user. This method involves waiting for the complete image file to download from the server before rendering it on the webpage or within the app.
Progressive Image Loading: Progressive image loading is an alternative technique that aims to improve the perceived loading time for users by initially displaying a lower-resolution or compressed version of the image.
Implementation of Progressive Image Loading
The provided code snippet demonstrates the implementation of progressive image loading in React Native. Let’s break it down and explain it from a progressive image loading perspective:
<Image
style={styles.image}
source={{ uri: filePath }}
progressiveRenderingEnabled={true}
onLoadEnd={() => setShowThumbnail(false)}
/>
This part of the code represents the main image component responsible for displaying the full-resolution image. Here’s what each attribute does:
style={styles.image}
: Specifies the styling for the image component, which can be defined in thestyles
object.source={{ uri: filePath }}
: Sets the source of the image to thefilePath
variable, which represents the URI of the image to be loaded.progressiveRenderingEnabled={true}
: Enables progressive rendering for the image, indicating that the image will be loaded progressively rather than all at once.onLoadEnd={() => setShowThumbnail(false)}
: When the image finishes loading, this callback function sets theshowThumbnail
state variable tofalse
, indicating that the thumbnail or lower-resolution image can be hidden.
{showThumbnail ? (
<Image
style={[
styles.image,
{
position: 'absolute',
top: 0,
},
]}
source={{ uri: filePath.replace('/-/-/', '/50/50/') }}
resizeMode="contain"
progressiveRenderingEnabled={true}
/>
) : null}
This part of the code handles the display of the thumbnail or lower-resolution image, depending on the value of the showThumbnail
state variable. Here's what it does:
{showThumbnail ? ... : null}
: Checks theshowThumbnail
state variable. If it'strue
, it renders the following JSX code. Otherwise, it rendersnull
.<Image ... />
: Renders anotherImage
component, which represents the thumbnail or lower-resolution image.style={[styles.image, { position: 'absolute', top: 0 } ]}
: Specifies the styling for the thumbnail image. It likely inherits the same styles as the main image but may have additional positioning properties to overlay it on top of the main image.source={{ uri: filePath.replace('/-/-/', '/50/50/') }}
: Sets the source of the thumbnail image to a modified version of thefilePath
. Thereplace
method is used to replace a portion of the URI with a smaller resolution version, indicated by'/50/50/'
.resizeMode="contain"
: Defines how the thumbnail image should be resized to fit within its container, ensuring that the entire image is visible without distortion.progressiveRenderingEnabled={true}
: Enables progressive rendering for the thumbnail image, indicating that it will load progressively.
Final result of all code looks like this:
<Image
style={styles.image}
source={{ uri: filePath }}
progressiveRenderingEnabled={true}
onLoadEnd={() => setShowThumbnail(false)}
/>
{showThumbnail ? (
<Image
style={[
styles.image,
{
position: 'absolute',
top: 0,
},
]}
source={{ uri: filePath.replace('/-/-/', '/50/50/') }}
resizeMode="contain"
progressiveRenderingEnabled={true}
/>
) : null}
In summary, the code implements progressive image loading by initially displaying the lower-resolution thumbnail image and progressively replacing it with the full-resolution image once it finishes loading. This approach helps improve the perceived loading time for users and provides a smoother and more engaging experience.
Follow Me
If you want to be part of a team that tries new technologies and want to experience a new challenge every day, come to us.