1
votes

I'm using Next.js for server-side rendering of a new project. I have a basic project with routing up and running. I'm using TypeScript, but I doubt it matters.

I'd like to have images lazy loaded, but I didn't find any component that supports lazy loading images for server-side rendering.

Here's how I see things at the moment -

Server-side rendering will respond to the client with the HTML template of the first rendering of the page.

On the client-side, once the HTML is loaded, images will begin lazy loading according to client-side logic.

I'm very new to server-side rendering, I imagine I'll have a similar issue with other functionalities that should be rendered at run-time on the client-side.

I'm trying to understand how to separate the logic for the first rendering which occurs on server-side and further dynamic rendering which I'd like to happen afterwards on the client-side like any other single page application.

1
you can’t use getInitialProps on child components in next.js. jaketrent.com/post/nextjs-getinitialprops-components - Corey Sax

1 Answers

0
votes

I think the way you see it is right, for client side code (like lazyload) I often use the componentDidMount lifecycle methods.

A HOC like this should do the trick.

function lazyLoadImages(selector = 'img') {
    function createObserver() {
        const elements = document.querySelectorAll(selector);
        const observer = new window.IntersectionObserver((entries, observerChild) => {
            entries.forEach((entry) => {
                if (entry.isIntersecting && entry.target.getAttribute('data-src')) {
                    entry.target.src = entry.target.getAttribute('data-src');
                    entry.target.removeAttribute('data-src');
                    observerChild.unobserve(entry.target);
                }
            });
        }, {});

        Array.prototype.map.call(elements, function(item) {
            observer.observe(item);
        });
    }

    if (!('IntersectionObserver' in window)) {
        const polyfill = document.createElement('script');
        polyfill.src = 'https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver';
        document.head.appendChild(polyfill);

        polyfill.onload = () => {
            createObserver();
        };
        return;
    }

    createObserver();
}

const LazyLoadHOC = (Page) => {
    return class extends React.Component {
        static getInitialProps(ctx) {
            if(Page.getInitialProps)
                return Page.getInitialProps(ctx);
        }

        componentDidMount() {
           lazyLoadImages('img');
        }

        render() {
            return <Page {...this.props}/>
        }

    }
}

Now you can wrap your pages with the LazyLoadHOC and replace src with data-src for all images you want to lazy load.