{"id":28068,"date":"2023-08-04T07:00:00","date_gmt":"2023-08-04T14:00:00","guid":{"rendered":"http:\/\/Lazy-load-Videos-in-Next.js-Pages"},"modified":"2025-02-15T14:17:14","modified_gmt":"2025-02-15T22:17:14","slug":"lazy-load-videos-in-next-js-pages","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages","title":{"rendered":"Lazy-Load Videos in Next.js Pages"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Over time, engineering efforts have been put into developing more performant web applications. <a href=\"https:\/\/jamstack.org\/\">Jamstack<\/a> technologies like <a href=\"https:\/\/nextjs.org\/\">Next.js<\/a> came from this need.<\/p>\n<p><a href=\"https:\/\/jamstack.org\/\">JAMstack<\/a> technologies like <a href=\"https:\/\/nextjs.org\/\">Next.js<\/a> emerged from an interest in improving the performance of modern web applications.<\/p>\n<p>This article will show you one way to improve your application performance by lazy-loading video assets on a Next.js page.<\/p>\n<h2>Before You Start<\/h2>\n<p>Next.js is a modern React.js framework for building fast web pages and web applications. Next.js touts numerous features to improve both developer experience, and web application performance.<\/p>\n<p>Lazy-loading is a technique in software engineering that requires delaying fetching\/loading resources until you need them. The loading activity can be triggered by different user actions, including scrolling, hovering, or highlighting.<\/p>\n<p>Lazy-loading shaves off any kilobytes contributing to page load times or network resources. It\u2019s currently used to defer images and JavaScript modules during runtime.<\/p>\n<p>Now, as a page loads, videos load a placeholder image along with a portion of the video. Once the video is played, the remaining part of the video is buffered.<\/p>\n<p>We\u2019ll shave off the page kilobytes loaded by the video, and defer loading until the video is scrolled into view, using the web intersection observer API. <a href=\"https:\/\/www.npmjs.com\/package\/react-intersection-observer\">React-intersection-observer<\/a> is a React.js implementation for an intersection observer, which informs us when a DOM element is in view.<\/p>\n<h2>Sandbox<\/h2>\n<p>We completed this project in <a href=\"https:\/\/codesandbox.io\/s\/mj-6-lazyload-videos-in-nextjs-d5gwq?file=\/pages\/index.js:0-533\">CodeSandbox<\/a>, and you can fork it to run the code.<\/p>\n<\/div>\n\n\n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/mj-6-lazyload-videos-in-nextjs-d5gwq?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"Lazy load videos in Next.js\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n\n<div class=\"wp-block-cloudinary-markdown \"><h2>Prerequisites and Installation<\/h2>\n<p>Having some knowledge of, and experience with, JavaScript and React.js is required for this project. You don\u2019t need to know Next.js in-depth, either.<\/p>\n<p>We\u2019ll install Next.js globally on our computer using yarn with the CLI command.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    yarn create next-app\n<\/code><\/span><\/pre>\n<p>This command opens a prompt to enter the project title. We\u2019ll use a project name of our choice, and yarn will install all dependencies required to bootstrap a Next.js project with the default starter.<\/p>\n<p>Find more <a href=\"https:\/\/nextjs.org\/docs\/getting-started\">methods to install Next.js here<\/a>.<\/p>\n<p>With the new project created, we\u2019ll install two dependencies. We\u2019ll need the <a href=\"https:\/\/www.npmjs.com\/package\/cloudinary-react\">Cloudinary-React<\/a> library to use Cloudinary\u2019s robust video player, and <code>react-intersection-observer<\/code>.<\/p>\n<p>In the project directory, we\u2019ll install these packages using yarn in the CLI with:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    yarn add cloudinary-react react-intersection-observer\n<\/code><\/span><\/pre>\n<p>With these installed, we\u2019ll start a development server for the project using the CLI command, <code>yarn dev<\/code>.<\/p>\n<h2>Page Creation<\/h2>\n<p>Like most modern frontend development frameworks, pages are created automatically using files in a designated folder. This is true for Next.js, too. The project\u2019s home page is located in <em>pages\/index.js<\/em> in the project\u2019s root directory.<\/p>\n<p>We\u2019ll need to create long-form content on the homepage so that we can sufficiently handle the scrolling activity. To do this, we\u2019ll make a content block component file titled <em>ContentBlock.jsx<\/em> with dummy text in <code>src\/components<\/code>. In this file, we\u2019ll create a function component with the following:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> ContentBlock = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ title = <span class=\"hljs-string\">\"Part\"<\/span> }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>{title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nisl\n            nunc, commodo quis faucibus non, venenatis nec mi. Praesent velit neque,\n            gravida at felis a, pulvinar sollicitudin metus. Vestibulum quis\n            vehicula metus. Maecenas tincidunt est ex, nec volutpat enim gravida\n            vitae. Donec dui orci, lacinia nec suscipit vitae, suscipit eget nulla.\n            Pellentesque ipsum velit, accumsan vel vehicula et, convallis at eros.\n            Fusce ac elit id ante varius posuere vitae eget enim. Nunc enim ex,\n            sodales vitae suscipit eget, dictum ac ante. Pellentesque tincidunt\n            vehicula pellentesque. Aliquam posuere et turpis non tincidunt.\n            Curabitur rhoncus euismod ante euismod cursus. Donec quis arcu eu sem\n            mattis fringilla non et augue. Integer lorem purus, sollicitudin vel\n            egestas et, mattis efficitur metus. Nam consectetur faucibus sem varius\n            lacinia.\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            Duis vitae ultricies tortor. In hac habitasse platea dictumst. Praesent\n            a ante sit amet ante mollis mattis ac nec nibh. Ut id tristique leo.\n            Etiam lobortis lacinia purus at semper. In mattis tincidunt leo, non\n            laoreet neque elementum vel. Nam ullamcorper lacus massa, a finibus\n            neque auctor eget. Sed sapien felis, tempus quis tempor vel, ultrices eu\n            lorem. Aenean in egestas tellus. Proin velit ante, suscipit in dui sit\n            amet, maximus faucibus massa. Donec porttitor ante dolor, a convallis\n            ligula mollis id. Curabitur suscipit nisi nec velit ornare porta. In\n            erat odio, blandit et iaculis at, ultrices a sapien. Suspendisse\n            potenti.\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This component will receive a prop of <code>title<\/code> with which we\u2019ll set the content title, and it\u2019ll render the set title and dummy text. We\u2019ll also add a default title of <code>Part<\/code> through the prop.<\/p>\n<p>This content block component will be used multiple times on the home page to create a scrollable page.<\/p>\n<p>In the home page component located in <code>pages\/index.js<\/code>, we\u2019ll use the <code>ContentBlock<\/code> component to render multiple contents.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> { ContentBlock } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/src\/components\/ContentBlock\"<\/span>;\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">IndexPage<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>Lazyloading videos in Next.js apps<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>We'll make a long page with text content<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 1\"<\/span> \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 2 - After the video player\"<\/span> \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 3\"<\/span> \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 4 - After the video player\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Creating the Video Player Component<\/h2>\n<p>We\u2019ll need to create a video player component, which we\u2019ll use on the home page or any other page where it\u2019s required. In <code>src\/components<\/code> we\u2019ll create a new component file named <code>VideoPlayer.jsx<\/code>. In the video player file, we\u2019ll first import the required dependencies and create a <a href=\"https:\/\/reactjs.org\/docs\/hooks-reference.html#usememo\">memoized<\/a> video player component.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> { Video, CloudinaryContext } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"cloudinary-react\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { useState, useEffect, memo } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { useInView } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-intersection-observer\"<\/span>;\n    \n    <span class=\"hljs-keyword\">const<\/span> MemoVidPlayer = memo(<span class=\"hljs-function\">(<span class=\"hljs-params\">{ publicId }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CloudinaryContext<\/span> <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">\"chuloo\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Video<\/span> <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">{publicId}<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"600px\"<\/span> <span class=\"hljs-attr\">controls<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">CloudinaryContext<\/span>&gt;<\/span><\/span>\n      );\n    });\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the snippet above, we created a video player using the <code>Video<\/code> component in the Cloudinary-React package. This <code>Video<\/code> component accepts a prop of <code>publicId<\/code>, a unique identifier for the video on Cloudinary. This way, we can serve remote optimized images hosted on Cloudinary.<\/p>\n<p>You can <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">create a Cloudinary account<\/a> to store, retrieve, and perform transformations on media assets, including videos. For this project, it\u2019s essential to note that we can replace the <code>Video<\/code> component used here with any other video element, including the HTML <code>video<\/code> element.<\/p>\n<p><code>CloudinaryContext<\/code> is a wrapper component that provides data passed as props to all of its child Cloudinary components. In this wrapper, we\u2019ll pass the cloud name of our Cloudinary account. You can obtain this from your Cloudinary dashboard after creating an account.<\/p>\n<p>The video player is returned in a memo function to ensure that the component doesn\u2019t render again, so long as its content and data don\u2019t change.<\/p>\n<p>In <code>VideoPlayer.jsx<\/code>, we\u2019ll create an exported function that we\u2019ll use on the home page. This function will handle the lazy loading of the previously created <code>MemoVidPlayer<\/code> component.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> VideoPlayer = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ vidPublicId = <span class=\"hljs-string\">\"video-blog\/cat\"<\/span> }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> &#91;publicId, setPublicId] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n      <span class=\"hljs-keyword\">const<\/span> { ref, inView } = useInView({ <span class=\"hljs-attr\">threshold<\/span>: <span class=\"hljs-number\">1<\/span> });\n      useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        <span class=\"hljs-keyword\">if<\/span> (inView === <span class=\"hljs-literal\">true<\/span>) {\n          setPublicId(vidPublicId);\n        }\n      }, &#91;inView, vidPublicId]);\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{ref}<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">MemoVidPlayer<\/span> <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">{publicId}<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the <code>VideoPlayer<\/code> component, we\u2019ll create a state variable to store the video\u2019s <code>publicId<\/code>. The video player will not load if the public ID is wrong or a source is not provided. This is a similar pattern used in lazy-loading images. We\u2019ll update the source of the video once it scrolls in view.<\/p>\n<p>Next, we\u2019ll destructure the <code>ref<\/code> and <code>inView<\/code> values from the <code>useInView<\/code> hook imported from <code>react-intersection-observer<\/code>. The <code>ref<\/code> value is assigned to the DOM element to be tracked, and <code>inView<\/code> returns a boolean value to show when the tracked element is in view. The <code>threshold<\/code> option ranges from 0 to 1, showing how much of the tracked element needs to be in view to trigger a change in the <code>inView<\/code> value.<\/p>\n<p>In a <code>useEffect<\/code> hook, we\u2019ll check if <code>inView<\/code> is true before updating the state value from an empty public ID to the video\u2019s specified public ID. The <code>useEffect<\/code> hook takes a dependency array of values that would trigger the component\u2019s rerender if it changes.<\/p>\n<p>For our <code>VideoPlayer<\/code> component, we\u2019ll set a default public ID of <code>video-blog\/cat<\/code>.<\/p>\n<p>Lastly, we\u2019ll update the home page component to show the video player, with each video having a different public ID from the other.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> { VideoPlayer } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/src\/components\/VideoPlayer\"<\/span>;\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">IndexPage<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>Lazyloading videos in Next.js apps<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>We'll make a long page with text content<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 1\"<\/span> \/&gt;<\/span>\n           <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">VideoPlayer<\/span> \/&gt;<\/span>\n           <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 2 - After the video player\"<\/span> \/&gt;<\/span>\n           <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">VideoPlayer<\/span> <span class=\"hljs-attr\">vidPublicId<\/span>=<span class=\"hljs-string\">\"video-blog\/kitten\"<\/span> \/&gt;<\/span>\n           <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 3\"<\/span> \/&gt;<\/span>\n           <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">VideoPlayer<\/span> <span class=\"hljs-attr\">vidPublicId<\/span>=<span class=\"hljs-string\">\"video-blog\/doggo\"<\/span> \/&gt;<\/span>\n           <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ContentBlock<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Part 4 - After the video player\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here\u2019s how the final page looks.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_7FB57D31BD5DE6CA6284E798571336FF247DC0289FD1C1BA5041077D4E40A9F0_1617546910674_Apr-04-2021+18-34-26.gif\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"480\" height=\"318\"\/><\/p>\n<p>We\u2019ll also check the browser console to see the loaded media assets in the network tab. The network tab shows the deferred loading of the video thumbnail and the preloaded video.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_7FB57D31BD5DE6CA6284E798571336FF247DC0289FD1C1BA5041077D4E40A9F0_1617547432104_Apr-04-2021+18-41-17.gif\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"480\" height=\"528\"\/><\/p>\n<h2>Summary<\/h2>\n<p>This article discussed the importance of lazy-loading media assets for performance improvements on modern web pages. We also walked through how to lazy-load a video component on a page using the react-intersection-observer package. Experiment with lazy-loading multiple videos on your pages for improved performance benefits.<\/p>\n<p>Want to discuss the topic of this blog or have a question? Head over to the <a href=\"https:\/\/community.cloudinary.com\/\">Cloudinary Community forums<\/a> or <a href=\"https:\/\/discord.gg\/cloudinary\">Discord<\/a> and join the conversation!<\/p>\n<p>##Resources<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/nextjs.org\/docs\/getting-started\">Getting started with Next.js<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.npmjs.com\/package\/cloudinary-react\">Cloudinary-React library<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.npmjs.com\/package\/react-intersection-observer\">React-intersection-observer<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":87,"featured_media":30787,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,186,212,246,303],"class_list":["post-28068","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-lazy-loading","tag-next-js","tag-react","tag-video"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Lazy-Load Videos in Next.js Pages<\/title>\n<meta name=\"description\" content=\"This post discussed how to lazy-load videos in Next.js pages for improved web performance.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Lazy-Load Videos in Next.js Pages\" \/>\n<meta property=\"og:description\" content=\"This post discussed how to lazy-load videos in Next.js pages for improved web performance.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-08-04T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-15T22:17:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Lazy-Load Videos in Next.js Pages\",\"datePublished\":\"2023-08-04T14:00:00+00:00\",\"dateModified\":\"2025-02-15T22:17:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages\"},\"wordCount\":6,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Lazy Loading\",\"Next.js\",\"React\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2023\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages\",\"url\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages\",\"name\":\"Lazy-Load Videos in Next.js Pages\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA\",\"datePublished\":\"2023-08-04T14:00:00+00:00\",\"dateModified\":\"2025-02-15T22:17:14+00:00\",\"description\":\"This post discussed how to lazy-load videos in Next.js pages for improved web performance.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Lazy-Load Videos in Next.js Pages\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Lazy-Load Videos in Next.js Pages","description":"This post discussed how to lazy-load videos in Next.js pages for improved web performance.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages","og_locale":"en_US","og_type":"article","og_title":"Lazy-Load Videos in Next.js Pages","og_description":"This post discussed how to lazy-load videos in Next.js pages for improved web performance.","og_url":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages","og_site_name":"Cloudinary Blog","article_published_time":"2023-08-04T14:00:00+00:00","article_modified_time":"2025-02-15T22:17:14+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS-jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Lazy-Load Videos in Next.js Pages","datePublished":"2023-08-04T14:00:00+00:00","dateModified":"2025-02-15T22:17:14+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages"},"wordCount":6,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA","keywords":["Guest Post","Lazy Loading","Next.js","React","Video"],"inLanguage":"en-US","copyrightYear":"2023","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages","url":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages","name":"Lazy-Load Videos in Next.js Pages","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA","datePublished":"2023-08-04T14:00:00+00:00","dateModified":"2025-02-15T22:17:14+00:00","description":"This post discussed how to lazy-load videos in Next.js pages for improved web performance.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/lazy-load-videos-in-next-js-pages#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Lazy-Load Videos in Next.js Pages"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1690224686\/Blog-lazyload_NextJS\/Blog-lazyload_NextJS.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28068","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28068"}],"version-history":[{"count":5,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28068\/revisions"}],"predecessor-version":[{"id":36814,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28068\/revisions\/36814"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/30787"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28068"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28068"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28068"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}