Create beautiful placeholders for your images

Published at:Published at:Updated at:

Have you ever faced the situation where the layout of your beautifully crafted interface “breaks” if the image (depeding on the quality of your user’s connections) takes some time to load? Something like the example below:

This happens because the browser has no clue about the dimensions of the image you want to display on your content beforehand.

The easiest way to solve this issue is using the aspect-ratio property to tell the browser how much space (based on the user’s size display) it should reserve before the image is loaded. Check the difference:

img {
/* Makes all images responsive */
  width: 100%;
  height: auto;

/*
  Here, I'm using the image's width and height,
  but you can come up with diference aspect ratios.
*/
  aspect-ratio: 760 / 235;
}

So, it solves the sudden layout change, but we can do even better do better adding an animated background.

Displaying an animated background

You can give a hint for the user that the blank space on your app should be filled with something by adding a background color or animating the transition between two o more colors, like in the example below:

And our code will look like this:

:root {
  /*
  Set the default aspect ratio. You can change
  this CSS variable per class/id or event
  creating diferente classes.
  */
  --aspect-ratio: 16/9;
}

img {
/* Makes all images responsive */
  width: 100%;
  height: auto;
}

/* Put all your images inside this container */
.image-container {  
  aspect-ratio: var(--aspect-ratio);
  position: relative;
  animation: background-loading .8s linear infinite alternate;
}

.image-container img {
  position: absolute;
  top: 0;
  left: 0;
}

/* The placeholder animation */
@keyframes background-loading {
  0% {
    background-color: #f9fafb;
  }
  100% {
    background-color: #d1d5db;
  }
}

And this is the result (check the code on CodePen):

Yet, this can be even better by displaying a colorful background that matches the image colors.

Displaying colorful image placeholders

BlurHash is a compact representation of a placeholder for a image. You use it to process your image before sending it to the browser and you’ll get a string of 20-30 characters that the algorithm can turn into a blurred image that you can show to your user before the actual image is downloaded. Check how it looks like:

I have implemented that last effect in React for the sake of simplicity and time, but you can re-implement it in whatever framework you like. Just pay attention to the onLoad event that changes the opacity of the image.

How to extend HTML elements with React

Published at:Published at:Updated at:

Most of the work needed to create custom HTML elements that fit the design system of your company resides styling and adding your own props. So, let’s say you have to create a custom Button, that should receive a children prop and should have DOM access via ref. That’s how you can do it:

import { forwardRef } from 'react';

type ButtonProps = {
  loading?: boolean; // custom prop
} & React.PropsWithChildren<React.ComponentPropsWithRef<'button'>>;

const Button: React.FC<ButtonProps> = forwardRef(
  ({ loading, children, ...props }, ref) => {
    return (
      <button data-loading={loading} {...props} ref={ref}>
        {children}
      </button>
    );
  }
);

export default Button;

We use the PropsWithChildren generic interface that gives the children prop and receive React.ComponentPropsWithRef<'button'>, that passes all props that a button can receive.

Of course, you can change the interface ComponentPropsWithRef for ComponentPropsWithoutRef and drop the forwardRef function on the definition of your component (although, I do not recomend it - refs may be useful later on your application):

type ButtonProps = {
  loading?: boolean; // custom prop
} & React.PropsWithChildren<React.ComponentPropsWithoutRef<'button'>>;

const Button: React.FC<ButtonProps> = ({ loading, children, ...props }) => {
  return (
    <button data-loading={loading} {...props} ref={ref}>
      {children}
    </button>
  );
};

export default Button;

You may, even, drop the interface PropsWithChildren, but on doing that, you’d have to implement the children prop by yourself:

type ButtonProps = {
  loading?: boolean; // custom prop
  children?: React.ReactNode;
} & React.ComponentPropsWithoutRef<'button'>;

const Button: React.FC<ButtonProps> = ({ loading, children, ...props }) => {
  return (
    <button data-loading={loading} {...props} ref={ref}>
      {children}
    </button>
  );
};

export default Button;

Want more? Check the live implementation on StackBlitz