# BlImageAv
BlImageAv is a Vue component that render image with ton of features
Differences with BlIllustraionAv
More versatile, Not only for illustration (PNG images), Zero CLS (Comulative Layout Shift)
# Features
- Lazy loading
- Zero CLS (Cumulative Layout Shift)
- Adaptive size based on viewport (via srcset and sizes)
- Retina support
- Server Side Rendering (SSR)
- Support Imagery
- Support auto Webp
- Support Webp incompatible browser (Safari, mobile browser)
- Support Eager loading
- Async decoding
- Support several layout types
# Background
See this RFC (opens new window) for more detailed
# Goal
Standardize image implementation across Bukalapak Web to improve performance and make it easier to setup.
This component by default will use Imagery as an image provider to resize and reformat on the fly
This component heavily inspired by Next.js Image (opens new window)
# Props and Events
# Default import BlImageAv
Props
Prop | Type | Required | Description | Default |
---|---|---|---|---|
src | String | true | Use image from Imagery | - |
alt | String | true | alt text image | - |
width | String/Number | true, expect when using layout="fill" or layout="responsive" | Width of the image when rendered | - |
height | String/Number | true, expect when using layout="fill" or layout="responsive" | Height of the image when rendered | - |
srcPlaceholder | String | false | image before actual image loaded | data:image/gif placeholder |
sizes | String | false | sizes attribute of img tag, ex. 80vw | undefined |
intersectionOptions | Object | false | The Intersection Observer options (opens new window) object. | () => ({}) |
imgClass | String | false | class name will be forwared to img tag | undefined |
layout | String (options: intrinsic , fixed , fill and responsive ) | false | see this section for examples | intrinsic |
objectFit | String | false | css object-fit usually being used with layout="fill tag | undefined |
objectPosition | String | false | css object-position usually being used with layout="fill tag | undefined |
unoptimized | Boolean | false | using original image URL instead resized one | false |
isEager | Boolean | false | Eagerly load image no matter intersected or not, good for image that immidietly shown to user without any interaction (LCP) | false |
maxSrcsetVariant | Number | false | you can limit number of width variant of srcset attribute, useful when you need to match with fixed preloaded image, more about preload in the bottom section | 5 |
Event
Event | Callback | Description |
---|---|---|
load | name | Emit event when image loaded |
error | name | Emit event when image faile |
# Usage
- App.vue
<template>
<div>
<bl-image-av
src="https://s0.bukalapak.com/athena/1625073466666/original/BANNER_DWEB_Flash_Diskon_Des.jpg"
width="325"
height="81.5"
img-class="simple-demo"
/>
</div>
</template>
<script>
import BlImageAv from '@bukalapak/bazaar-visual/dist/components/BlImageAv';
export default {
name: 'BlImages',
components: {
BlImageAv,
},
};
</script>
# Layout Types
The layout behavior of the image as the viewport changes size.
layout | Behavior | srcSet | sizes |
---|---|---|---|
intrinsic (default) | Scale down to fit width of container, up to image size | 1x , 2x (based on imageSizes) | N/A |
fixed | Sized to width and height exactly | 1x , 2x (based on imageSizes) | N/A |
responsive | Scale to fit width of container | 640w , 750w , ... 2048w , 3840w (based on imageSizes and deviceSizes) | 100vw |
fill | Grow in X and Y axes to fill container | 640w , 750w , ... 2048w , 3840w (based on imageSizes and deviceSizes) | 100vw |
Demo the intrinsic layout (default)
When intrinsic, the image will scale the dimensions down for smaller viewports, but maintain the original dimensions for larger viewports.
<bl-image-av src="https://s0.bukalapak.com/athena/1625073466666/original/BANNER_DWEB_Flash_Diskon_Des.jpg" width="325" height="81.5" img-class="simple-demo rounded" />
Demo the fixed layout
- When fixed, the image dimensions will not change as the viewport changes (no responsiveness) similar to the native img element.
<bl-image-av src="https://s4.bukalapak.com/rev-banner/flash_banner/348980120/original/mobile_FashionFestival_8f211f94-700a-480b-9e08-1ffaafcdee6a.jpeg" width="150" height="150" alt="this alt text" layout="fixed" />
- When fixed, the image dimensions will not change as the viewport changes (no responsiveness) similar to the native img element.
Demo the responsive layout
When responsive, the image will scale the dimensions down for smaller viewports and scale up for larger viewports. Ensure the parent element uses display: block in their stylesheet.
<bl-image-av width="140" height="78" layout="responsive" src="https://s1.bukalapak.com/promo/promo_banner/original/ziUS2itRwenNVzTPhvXHVV-2880x414_px.jpg" />
TIP
Resize your window to see the responsive layout
Demo the fill layout
When fill, the image will stretch both width and height to the dimensions of the parent element, provided the parent element is relative.
This is usually paired with the objectFit property. Ensure the parent element has position: relative in their stylesheet.
<div style="position: relative; width: 400px; height: 200px" :style="{ border: '2px solid red' }"> <bl-image-av layout="fill" src="https://s0.bukalapak.com/athena/1625073466666/original/BANNER_DWEB_Flash_Diskon_Des.jpg" object-fit="contain" object-position="right top" /> </div>
# Use cases
# Preload image
To optimize Webvitals LCP (Largest Contenful Paint), we usually try to preload the largest image in the page, by adding link meta tag like this (opens new window), the problem is we may load two different image size (not match between image we preload and we display at the client)
How to make them match then? Use same utility function from BlImageAv to generate image URL
const BlImageAv = require('@bukalapak/bazaar-visual/src/components/BlImageAv/utils');
const linkMeta = {};
const imageAttrs = BlImageAv.generateImgAttrs({
src: mainImageToPreload,
width: MAIN_BANNER_SIZE.width,
height: MAIN_BANNER_SIZE.height,
layout: 'intrinsic',
});
linkMeta[`main-banner-image`] = `<link rel="preload" href="${imageAttrs.src}" as="image">`;
return linkMeta;
example : meta hook (opens new window)
Then, set maxSrcsetVariant
prop in BlImageAv
to 1
, so the srcset
attribute will only have one option, so browser won't load differnt image size.
example : banner component (opens new window)
# Limitations
# Imagery currently doesn't support PNG image resize
Workaround to this limitation is by passing unoptimized
prop, so we will use the original version one.