Take control over your media loading

Matti Bar-Zeev - Mar 19 - - Dev Community

MediaLoader is a versatile React component that provides fine-grained control over the loading of media assets such as images, videos, and audio files. It offers a customizable loading strategy, allowing developers to prioritize resources and optimize user experience.

It has been a while, I know.
Hopefully I have something for you that will compensate for my absence 🙂

As you may already know, web performance has become increasingly significant within the frontend community. Those who value their work and prioritize user experience dedicate considerable effort to enhancing the performance of their websites and applications.

Improving performance involves loading only essential resources, allowing the network connection to prioritize critical assets and expedite the page's readiness and interactivity.
Media content—such as images, videos, and audio—typically constitutes the bulk of data to be loaded. Moreover, when the browser encounters a media tag during document parsing, it initiates immediate loading by default.

The "lazy" attribute for the img tag aims to alleviate network load by enabling the deferred loading of images until necessary. When you use the lazy attribute, the browser will only load the image when it enters the viewport, helping to conserve bandwidth and improve page loading times, especially on pages with many images.

However, this is insufficient. We recognize that viewport entry alone may not always suffice as the desired trigger. There are instances where we might prefer loading based on observing a CSS property or clicking an element, among other examples. Additionally, it's important to note that images are not the sole type of media content we aim to lazy-load, yet they unfortunately lack support for such functionality.

This is where the MediaLoader comes in to fill the gap.

MediaLoader is a versatile React component that provides fine-grained control over the loading of media assets such as images, videos, and audio files. It offers a customizable loading strategy, allowing developers to prioritize resources and optimize user experience.

With MediaLoader, you can effortlessly manage the loading process, dynamically loading media content based on user interactions, viewport visibility and much more. Whether you're building a gallery, a multimedia-rich website, or an application with heavy media content, MediaLoader empowers you to deliver a seamless and performant user experience.

MediaLoader is designed to be as non-intrusive as possible, integrating into your existing React applications without imposing a heavy footprint and without changing the DOM structure. It provides a lightweight wrapper around your current media, without requiring a complete overhaul of your codebase.

You can check out some demo examples in this Storybook, and see the code in the pedalboard repo.

How do I use it?

You first need to install it:

yarn add @pedalboard/media-loader
Enter fullscreen mode Exit fullscreen mode

Or

npm install @pedalboard/media-loader
Enter fullscreen mode Exit fullscreen mode

Now, say you have this JSX:

<img src="/assets/image-04.jpg" alt="image-04"></img>
<img src="/assets/image-05.jpg" alt="image-05"></img>
<img src="/assets/image-06.jpg" alt="image-06"></img>
<div>
    <img src="/assets/image-09.jpg" alt="image-09"></img>
</div>
Enter fullscreen mode Exit fullscreen mode

All you need to do is to wrap it with the MediaLoader component and provide a loading strategy to it, like so:

import MediaLoader from '@pedalboard/media-loader';
...

<MediaLoader
    loadingStrategy={(mediaHTMLElementRefs, loadMedia) => {
        // You loading strategy here
    }}
>
    <img src="/assets/image-04.jpg" alt="image-04"></img>
    <img src="/assets/image-05.jpg" alt="image-05"></img>
    <img src="/assets/image-06.jpg" alt="image-06"></img>
    <div>
        <img src="/assets/image-09.jpg" alt="image-09"></img>
    </div>
</MediaLoader>
Enter fullscreen mode Exit fullscreen mode

The loadingStrategy is a function which receives 2 arguments - An array of the media element refs and the loadMedia function. When you call the loadMedia function with a given ref it will load the associated media for it, whether it is an image, video or audio.

Your loading strategy might be something like this:

loadingStrategy={(mediaHTMLElementRefs, loadMedia) => {
                       const intersectionCallback = (entries) => {
                           entries.forEach((entry) => {
                               if (entry.isIntersecting) {
                                   let elem = entry.target;
                                   loadMedia(elem);
                               }
                           });
                       };
                       let options = {
                           rootMargin: '0px',
                           threshold: 1.0,
                       };
                       let observer = new IntersectionObserver(intersectionCallback, options);


                       mediaHTMLElementRefs.forEach((mediaHTMLElementRef) => {
                           let target = mediaHTMLElementRef.current;
                           if (target) {
                               observer.observe(target);
                           }
                       });
                   }}

Enter fullscreen mode Exit fullscreen mode

In the example above we act the same as the lazy attribute of an img tag. Once the element intersects the viewport, we trigger the load.

You can now probably see the great control it allows you to have over your media content loading. Another example can be monitoring the left CSS property of the element and only when it surpasses 300, load it’s media content:

import MediaLoader from '@pedalboard/media-loader';
...

<MediaLoader
    loadingStrategy={(mediaHTMLElementRefs, loadMedia) => {
        const monitor = (target) => {
            const computedStyle = getComputedStyle(target.parentElement);
            const currentLeft = parseFloat(computedStyle.left);
            // If the image container reach a certain left then load it
            if (currentLeft > 300) {
                loadMedia(target);
                return;
            }

            // Continue monitoring in the next frame
            requestAnimationFrame(() => monitor(target));
        };

        mediaHTMLElementRefs.forEach((mediaHTMLElementRef) => {
            let target = mediaHTMLElementRef.current;
            if (target) {
                requestAnimationFrame(() => monitor(target));
            }
        });
    }}
>
    <div
        className="image-container"
        onClick={(event) => {
            (event.target as HTMLDivElement).classList.add('animate-media');
        }}
    >
        <img src="/assets/image-01.jpg" alt="image-09" className="run"></img>
        <div>And now me</div>
    </div>
    <div
        className="image-container"
        onClick={(event) => {
            (event.target as HTMLDivElement).classList.add('animate-media');
        }}
    >
        <img src="/assets/image-02.jpg" alt="image-09" className="run"></img>
        <div>Quick! Click me ;)</div>
    </div>
    <div
        className="image-container"
        onClick={(event) => {
            (event.target as HTMLDivElement).classList.add('animate-media');
        }}
    >
        <img src="/assets/image-03.jpg" alt="image-09" className="run"></img>
        <div>Click me ;)</div>
    </div>
</MediaLoader>
Enter fullscreen mode Exit fullscreen mode

You can read more about it in the project README file, download it from the NPM registry and start playing with it 🙂

I’d love to hear your thoughts and feedback on this project, and if it helped you produce a better performing web content.

Until next time,
Cheers!

Photo by USGS on Unsplash

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .