Recently I created a nice lazy-loading image effect for some drag and drop menu items in my app. Images in a side menu start off with a loading shimmer effect, and then fluidly grow to their correct height and fade in when loaded:
💡 Note: This effect is better for apps rather than images in articles, as changing heights within article content can cause a layout shift.
A closer look 🔍
Whilst there are plenty of useful React libraries that can lazy load images, for my use case where the images are stacked in a column, I needed a way to do this with a dynamic height:
As shown above, each image starts from a predefined minimum height, and then grows or shrinks depending on the actual image size.
In hindsight, maybe it would have been possible to calculate the height of the images before they had loaded (please let me know if you know how!).
Letter
Creating the Shimmer Effect, and adding (Framer) Motion
I’ve isolated this image lazy-loader into a single React component in CodeSandBox. Each time you press the refresh button (bottom left of sandbox), it’ll load a different image from Unsplash, so you can see how it adapts to differently sized images:
CodeSandbox Example
All in 40 lines:
The component is only 40 lines. The majority of it is React JSX markup – an image tag wrapped with a div element. And entire thing works from the image tag’s onLoad
attribute on line 31.
Once the browser has loaded the image, the imageLoaded
function is called, which changes the component state, and the image’s CSS class:
- ✨ CSS shimmer effect: on line 17, you’ll see the CSS classes:
'pulse'
, and'loadable'
. When the image is loading, the pulse class is added, creating a shimmer effect – that’s defined in the CSS file. - 🚀 Framer Motion animation: in lines 20-30, the image tag has been converted to a Framer motion tag, by adding the
motion.
tag decorator. Then the next few lines define the animation that will be triggered after the image is loaded. Learn more about Framer motion here.
And that’s it, short and sweet(ish). In all, everything depends on the image’s onLoad event. If you have any questions give me a message on Twitter, and I can try and help.
Discovering Framer Motion:
In this example, I used Framer Motion to animate the height of the image because I was already using it in my app for the sidebars, and really find it very intuitive. If you scroll back to the first image, the way the side panel opens up and bounces is created with Framer motion effects.
If you want to learn it, here’s how I got started using it – just by exploring other people’s creations in CodeSandbox. Animating a square is always a good starting point for me:
I usually struggled to make basic slideout menus, but those crazy animations are a few lines:
anim.start({ y:-400, scale:1.2 })
anim.start({ y: 200, scale:0.5 })
anim.start({ y:-100, rotateZ:-180, scale: 2})learned it from this sandbox by @pedropalhari https://t.co/9LXcLPkfm9
— graeme (@graeme_fulton) May 27, 2021
What do you use for lazy-loading images? How do you like animation libraries like Framer Motion? Good luck with making!