home.html contact.html projects.html blog.html
Blog

Lazy Loading Blurred Images in Astro

Today I will showcase how you can create a lazy blur up image effect on your images in Astro!

I am using this component in the following places as of now:

I will be using Tailwind CSS and Alpine.js for styling and scripting, but this can easily be recreated in vanilla JS/CSS if that’s your preference!

The concept is really simple:

  • Construct a container that is sized to your image dimensions
  • Render a microscopic version of the image with a tiny file size that loads eagerly first and displays while the full resolution image is loading
  • Load the full resolution image lazily, and once completed, hide the tiny image that is displaying in front of it

Take a look at this Astro component I named ImageBlur.astro:

---
import { Image } from 'astro:assets'

const {
  class: divClass = '',
  src,
  alt = '',
  width = undefined,
  height = undefined,
} = Astro.props
---

<!--
  Only show the blurred image if
  the other image is not loaded yet!
-->
<div
  class:list={[divClass, 'relative overflow-hidden']}
  x-data="{ showBlur: !$refs.image.complete }"
>
  <!--
    This is the tiny blurred image,
    positioned in front of the full resolution one!
    I am using alpine to animate this out
    when the other image finishes loading.
  -->
  <div
    class="absolute inset-0 z-10 size-full"
    x-show="showBlur"
    x-transition.opacity.duration.100ms
  >
    <!--
      Here is where we generate our tiny image
      with Astro Image, in webp, eagerly!
    -->
    <Image
      width={25}
      height={25}
      quality={50}
      format="webp"
      loading="eager"
      alt=""
      src={src}
      class="absolute inset-0 size-full object-cover object-center"
    />
  </div>
  <!--
    This is the full resolution image.
    When it finishes, we animate out the blurred one!
  -->
  <Image
    format="webp"
    x-ref="image"
    @load="() => showBlur = false"
    src={src}
    width={width}
    height={height}
    widths={widths}
    sizes={sizes}
    alt={alt}
    loading="lazy"
    class="absolute inset-0 z-0 size-full object-cover object-center"
  />
</div>

This can now be used in any other Astro files like so:

---
import ImageBlur from '@/components/ImageBlur.astro'
import exampleImage from '@/assets/example-image.jpg'
---

<ImageBlur src={exampleImage} class="" width={500} height={500} />

You can view the source code for this exact component on this very site by visiting the GitHub repository!

Last updated on 06/30/2025

Tags: Astro, Alpine, Tailwind

Hello! I’m Matt Waler, a website and application developer from Ohio. I focus on building clean, pragmatic, and maintainable web and mobile apps.

When I am not working, I am most likely playing tennis, lifting weights, or at the nearest Chipotle.