# 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"
      />
      
      this alt text
  • 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.