{"id":27874,"date":"2022-04-06T02:28:16","date_gmt":"2022-04-06T02:28:16","guid":{"rendered":"http:\/\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs"},"modified":"2022-04-06T02:28:16","modified_gmt":"2022-04-06T02:28:16","slug":"build-a-responsive-image-grid-with-chakra-ui-in-nextjs","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/","title":{"rendered":"Simulate a Streaming Event in Gatsby.js"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><h2>Introduction<\/h2>\n<p>Live streaming events have become more of the norm these past few years. They give a sense of urgency to your content, boost online interaction between you and your users, and help reach a wider audience.<\/p>\n<p>This tutorial will walk through handling video player events by building a live streaming event in Gatsby.js.<\/p>\n<h2>What we will be building<\/h2>\n<p>The live streaming event mimics live events, where we want to play a video starting from a specified time. The video starting position for the user is adjusted based on how much time has passed since the actual video started. For example, a video that is 1 hour long but started 45 minutes ago would have any new user that visits the page automatically start at the 45-minute mark on the video timeline.<\/p>\n<p>After the recorded video is complete, we want an overlay showing the text \u201cLive event has ended\u201d and our native player controls hidden.<\/p>\n<h2>CodeSandbox<\/h2>\n<p>We completed this project in a <a href=\"https:\/\/codesandbox.io\/s\/ecstatic-jennings-ndeiog?file=\/src\/pages\/index.js:116-128\">CodeSandbox<\/a>. To get started quickly, fork it or run the project.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>https:\/\/codesandbox.io\/s\/ecstatic-jennings-ndeiog?file=\/src\/pages\/index.js:116-128\n\n<\/code><\/pre>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/ecstatic-jennings-ndeiog?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=\"simulate a streaming event in gatsbyjs\"\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  <div class=\"wp-block-cloudinary-markdown \"><p>You can find the GitHub repository <a href=\"https:\/\/github.com\/Tundesamson26\/Gatsby.js-live-event\">here<\/a>.<\/p>\n<h2>Prerequisites<\/h2>\n<p>To get the most out of this tutorial, you\u2019ll need the following:<\/p>\n<ul>\n<li>A basic understanding of CSS, JavaScript, React, and GraphQL.<\/li>\n<li>Node and its package manager, <code>npm<\/code>. Run the command <code>node -v<\/code> &amp;&amp; <code>npm -v<\/code> to verify that you have them installed, or install them from <a href=\"https:\/\/nodejs.org\/en\/\">here<\/a>.<\/li>\n<li>Gatsby command-line interface (CLI) installed.<\/li>\n<li>Understanding Gatsby.js would help us follow through with this tutorial.<\/li>\n<\/ul>\n<h2>Setting up a Gatsby environment<\/h2>\n<p><a href=\"https:\/\/www.gatsbyjs.com\/\">Gatsby<\/a> a Static Site Generator, is a React-based open-source framework for creating websites and apps.<\/p>\n<p>To create our Gatsby.js app, we\u2019ll go to our terminal or command prompt using the git <code>cd<\/code> command and perform the following steps:<\/p>\n<ol>\n<li>We need the Gatsby CLI on our computer; let\u2019s run this command:<\/li>\n<\/ol>\n<pre class=\"js-syntax-highlighted\"><code>    npm install -g gatsby-cli \n<\/code><\/pre>\n<ol start=\"2\">\n<li>Create a new site:<\/li>\n<\/ol>\n<pre class=\"js-syntax-highlighted\"><code>    gatsby new\n<\/code><\/pre>\n<p>It\u2019ll ask for a site title and the name of the project\u2019s directory. Continue following the prompts to choose our preferred language (JavaScript or TypeScript), CMS, styling tools, and additional features.<\/p>\n<ol start=\"3\">\n<li>Upon downloading everything, we\u2019ll see a message with instructions for navigating to our site and running it locally.<\/li>\n<\/ol>\n<p>The CLI created the site as a new folder with the name you chose in Step 1.\nStart by going to the directory with:<\/p>\n<pre><code>cd my-gatsby-site\n<\/code><\/pre>\n<p>Let\u2019s start the local development server with:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>    gatsby develop\n<\/code><\/pre>\n<p>Gatsby will start a hot-reloading development environment accessible by default at <code>http:\/\/localhost:8000<\/code>.<\/p>\n<ol start=\"4\">\n<li>Now we\u2019re ready to make changes to our site!<\/li>\n<\/ol>\n<p>Let\u2019s try editing the home page in <code>src\/pages\/index.js<\/code>.<\/p>\n<blockquote>\n<p>NOTE: Saved changes will automatically reload in the browser.<\/p>\n<\/blockquote>\n<h2>Installing dependencies<\/h2>\n<p>We will install React Player to handle our video and video player events in this part of the tutorial.<\/p>\n<h2>React Player<\/h2>\n<p><a href=\"https:\/\/www.npmjs.com\/package\/react-player\">React Player<\/a> is a React component for rendering a variety of video URLs. React Player allows us to create and personalize our video player with callback props for almost every player event. It also offers an excellent media experience in web applications.<\/p>\n<p>To install React Player in our app, we run the following command.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>npm install react-player\n# or \nyarn add react-player\n<\/code><\/pre>\n<h2>Creating the video player<\/h2>\n<p>To create our video player, we first create a <code>component<\/code> folder in the <code>src<\/code> of our app. In this folder, we will create our <code>video-player.js<\/code> file.<\/p>\n<p>To use the <code>ReactPlayer<\/code> component, we import it from the <code>react-library<\/code> into our <code>video-player.js<\/code> file.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>import React from 'react'\nimport ReactPlayer from 'react-player'\n<\/code><\/pre>\n<p>This <code>ReactPlayer<\/code> component allows us to use various props. We then pass values into these props to personalize our player.<\/p>\n<p>In the next piece of code, we use the <code>url<\/code> prop, the <code>controls<\/code> prop, and, finally, the <code>playing<\/code> prop.<\/p>\n<p>The <code>url<\/code> prop takes in a value that points to the media path we want to play. To display the native player controls, we use the <code>controls<\/code> prop. This prop takes in a Boolean value. When the <code>controls<\/code> prop is set to true, the controls are displayed and hidden when set to false.<\/p>\n<p>Next, we have the <code>playing<\/code> prop. The <code>playing<\/code> prop plays or pauses the media when set to true or false.<\/p>\n<p>The <code>ReactPlayer<\/code> component also allows us to set the width and height of the player using the <code>width<\/code> and <code>height<\/code> props, respectively.\nFailing to do so sets our video player to this default dimensions <code>width: 640px; height: 360px;<\/code> which we will use in this tutorial.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>import React from 'react';\nimport ReactPlayer from 'react-player';\nconst VideoPlayer = () =&gt; {\n    return (\n      &lt;div className=&quot;video-player&quot;&gt;\n        &lt;ReactPlayer\n          url=&quot;https:\/\/res.cloudinary.com\/beswift-hub\/video\/upload\/v1648105334\/videoplayback_rz8dhu.mp4&quot;\n          playing={true}\n          controls={true}\n        \/&gt;\n      &lt;\/div&gt;\n    );\n}\nexport default VideoPlayer;\n<\/code><\/pre>\n<p>By setting our <code>playing<\/code> and <code>controls<\/code> prop to true, our video player plays our video as it renders on our screen and displays our native player controls.<\/p>\n<p>Next, we import our video player to our <code>pages\/index.js<\/code> file to see our video player in our browser.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>\n\/\/ Step 1: Import React\nimport * as React from &quot;react&quot;;\nimport VideoPlayer from &quot;..\/component\/video-player&quot;;\n\/\/ Step 2: Define your component\nconst IndexPage = () =&gt; {\n  return (\n    &lt;main&gt;\n      &lt;title&gt;Live Streaming&lt;\/title&gt;\n      &lt;h1&gt;Welcome to my Gatsby site Streaming event&lt;\/h1&gt;\n      &lt;p&gt;I'm making a streaming event with Gatsby&lt;\/p&gt;\n      &lt;VideoPlayer\/&gt;\n    &lt;\/main&gt;\n  );\n};\n\/\/ Step 3: Export your component\nexport default IndexPage;\n<\/code><\/pre>\n<p>With that, we have our video player set up.<\/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_sanity\/ded82207d4cbea5e89f6c0cc6dfee58999cb5a6b-709x324.png\" alt=\"image\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"709\" height=\"324\"\/><\/p>\n<h2>Creating a Streaming event<\/h2>\n<p>To create a live streaming event in our app, we start by importing React and the <code>useState<\/code> hook in our <code>pages\/index.js<\/code> file.<\/p>\n<p>The <code>useState<\/code> hook allows us to add states to our functional components.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>import React, { useState } from &quot;react&quot;;\n<\/code><\/pre>\n<p>Next, using the <code>useState<\/code> hook, we create pieces of state to control different properties of our video player.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>const [startTime, setStartTime] = useState(1630430724714);\nconst [controls, setControls] = useState(true);\nconst [ended, setEnded] = useState(false);\nconst [duration, setDuration] = useState(null);\nconst [playing, setPlaying] = useState(true);\n<\/code><\/pre>\n<ul>\n<li>\n<code>startTime<\/code> : Set to an initial value of 1630430724714; this is the time in milliseconds we want our \u2018live event\u2019 to start (we shall discuss how to find the time in milliseconds in the functions section of this tutorial below).<\/li>\n<li>\n<code>controls<\/code> : Set to an initial value of true, this property sets the <code>controls<\/code> prop on the <code>ReactPlayer<\/code> component.<\/li>\n<li>\n<code>ended<\/code> : Set to an initial value of false. It will be set to true when the video ends. We\u2019ll use the <code>ended<\/code> variable to render our overlay conditionally.<\/li>\n<li>\n<code>duration<\/code> : Stores the duration of the video being played in seconds.<\/li>\n<li>\n<code>playing<\/code> : Controls when our video starts or stops playing. It is set to an initial value of <code>true<\/code>, as we want our video to be playing when it mounts, unless the video has ended.<\/li>\n<\/ul>\n<p>Next, our control functions.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>let date = new Date();\nlet currentTime = date.getTime();\nlet timePlayed = (currentTime - startTime) % 1000;\nconst endVideo = () =&gt; {\n  if (controls === false &amp;&amp; ended === true) {\n    if (playing === false) {\n      return;\n    } else {\n      setPlaying(false);\n    }\n  } else {\n    setControls(false);\n    setEnded(true);\n    setPlaying(false);\n  }\n};\nconst videoDuration = (num) =&gt; {\n  setDuration(num);\n};\nif (timePlayed &gt; duration) {\n  endVideo();\n}\nconst restartLive = () =&gt; {\n  let newDate = new Date();\n  let newStartTime = newDate.getTime();\n  setStartTime(newStartTime);\n  setEnded(false);\n  setPlaying(true);\n  setControls(true);\n};\n<\/code><\/pre>\n<h3><strong>Getting the current time in milliseconds<\/strong><\/h3>\n<p>The <code>date<\/code> variable in the code block above obtains the current date using the JavaScript <code>new Date()<\/code> method. We then get the current time in milliseconds using the JavaScript <code>getTime()<\/code> method and store it in the <code>currentTime<\/code> variable. The <code>timePlayed<\/code> variable gets the difference between the time our \u2018live event\u2019 started and the current time in seconds; this variable will be used to tell our video player what time in the player we want to seek to.<\/p>\n<h3><strong>What happens when our video ends?<\/strong><\/h3>\n<p>The <code>endVideo<\/code> function will get called when our video ends, even long after. The <code>endVideo<\/code> function has an <code>if else<\/code> block. This <code>if else<\/code> conditional checks whether the <code>controls<\/code> variable is already <code>false<\/code> and the <code>ended<\/code> variable <code>true<\/code>, if they are, we go further to check if the <code>playing<\/code> variable is false. if our code passes all these checks, then we want to return from the function.<\/p>\n<p>If, however, our code does not pass all these checks, we want to set<\/p>\n<ul>\n<li>\n<code>playing<\/code> to false to stop playing the video, and<\/li>\n<li>\n<code>controls<\/code> to false to hide the native player controls.<\/li>\n<\/ul>\n<p>We created a <code>videoDuration<\/code> function that takes in a <code>num<\/code> parameter, this parameter represents the duration of the media. This <code>videoDuration<\/code> function uses the <code>num<\/code> parameter to update the <code>duration<\/code> state variable.<\/p>\n<p>Next, we have an <code>if<\/code> block of code, this checks whether the <code>timePlayed<\/code> variable (containing how much time has passed since the start of the live) is greater than the <code>duration<\/code>. If it is, we run the <code>endVideo<\/code> function.<\/p>\n<h3><strong>Restarting the video<\/strong><\/h3>\n<p>The <code>restartLive<\/code> function is used to restart the live event simulation. The <code>newDate<\/code> variable gets the date that the <code>restartLive<\/code> function ran, and the <code>newStartTime<\/code> variable stores the exact time in milliseconds this function ran. We pass in the <code>new``S``tartTime<\/code> to the <code>setStartTime<\/code> function to reset the <code>startTime<\/code> variable.<\/p>\n<p>Finally, we set our <code>ended<\/code> variable to false, as we just started our simulation over again.<\/p>\n<p>Next, we add a button to run this <code>restartLive<\/code> function, with the <code>onClick<\/code> event handler.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>&lt;button className= 'reset-button' onClick = {restartLive}&gt;Restart Streaming event&lt;\/button&gt;\n<\/code><\/pre>\n<p>By now, our <code>pages\/index.js<\/code> should look like this:<\/p>\n<p><strong>The module imports and methods<\/strong><\/p>\n<pre class=\"js-syntax-highlighted\"><code>\/\/ Step 1: Import React\nimport React, { useState } from &quot;react&quot;;\nimport VideoPlayer from &quot;..\/component\/video-player&quot;;\n\/\/ Step 2: Define your component\nconst IndexPage = () =&gt; {\n\nconst [startTime, setStartTime] = useState(1630430724714);\nconst [controls, setControls] = useState(true);\nconst [ended, setEnded] = useState(false);\nconst [duration, setDuration] = useState(null);\nconst [playing, setPlaying] = useState(true);\n\nlet date = new Date();\nlet currentTime = date.getTime();\nlet timePlayed = (currentTime - startTime) % 1000;\nconst endVideo = () =&gt; {\n  if (controls === false &amp;&amp; ended === true) {\n    if (playing === false) {\n      return;\n    } else {\n      setPlaying(false);\n    }\n  } else {\n    setControls(false);\n    setEnded(true);\n    setPlaying(false);\n  }\n};\nconst videoDuration = (num) =&gt; {\n  setDuration(num);\n};\nif (timePlayed &gt; duration) {\n  endVideo();\n}\nconst restartLive = () =&gt; {\n  let newDate = new Date();\n  let newStartTime = newDate.getTime();\n  setStartTime(newStartTime);\n  setEnded(false);\n  setPlaying(true);\n  setControls(true);\n};\n<\/code><\/pre>\n<p><strong>The rendered component<\/strong><\/p>\n<pre class=\"js-syntax-highlighted\"><code>return (\n    &lt;main&gt;\n      &lt;title&gt;Live Streaming&lt;\/title&gt;\n      &lt;h1&gt;Welcome to my Gatsby site Streaming event&lt;\/h1&gt;\n      &lt;p&gt;I'm making a streaming event with Gatsby&lt;\/p&gt;\n      &lt;div className=&quot;live-event-container&quot;&gt;\n        {\/* Our VideoPlayer component *\/}\n        &lt;VideoPlayer\n          ended={ended}\n          timePlayed={timePlayed}\n          controls={controls}\n          endVideo={endVideo}\n          playing={playing}\n          videoDuration={videoDuration}\n        \/&gt;\n      &lt;\/div&gt;\n      {\/* Our Restart button *\/}\n      &lt;button className=&quot;reset-button&quot; onClick={restartLive}&gt;\n        Restart Streaming event\n      &lt;\/button&gt;\n    &lt;\/main&gt;\n  );\n};\n<\/code><\/pre>\n<h2>Passing the variable to our React player<\/h2>\n<p>We passed our <code>timePlayed<\/code> variable, <code>controls<\/code> variables, <code>endVideo<\/code> function, and our <code>videoDuration<\/code> function to the <code>VideoPlayer<\/code> component housing our <code>ReactPlayer<\/code> component.<\/p>\n<p>Create a <code>video-player.js<\/code> file in the <code>component<\/code> directory and paste the code below.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>&lt;VideoPlayer \ntimePlayed ={timePlayed} \ncontrols={controls} \nendVideo={endVideo} \nplaying={playing} \nvideoDuration={videoDuration} \n\/&gt;\n<\/code><\/pre>\n<p>In the <code>src\/component\/video-player.js<\/code> file, we define a <code>player<\/code> variable; we\u2019ll use this variable later to set the ref on our <code>ReactPlayer<\/code>, and we do this to reference the component.<\/p>\n<p>Next, we pass the variables we received to their respective props, the <code>controls<\/code> variable to the controls prop, and the <code>playing<\/code> variable to the playing prop.<\/p>\n<p>The <code>ReactPlayer<\/code> component allows us to use an <code>onEnded<\/code> callback prop, which runs the function passed into it when the video ends, so we pass in the <code>endVideo<\/code> function.<\/p>\n<p>The <code>onStart<\/code> callback prop takes a function that runs when the video is mounted and ready to play. We use this to call the <code>getDuration()<\/code> method on the <code>player<\/code> object we obtain from the <code>ReactPlayer<\/code> component. This method is used to retrieve the duration of the video in seconds.<\/p>\n<p>Next, we write another function using the <code>seekTo()<\/code> method on the <code>player<\/code> object. The <code>seekTo()<\/code> method takes in a time parameter in seconds that indicates where we want to skip to in the video. We then pass the <code>timePlayed<\/code> variable (the difference between the current time and the start time in seconds) as the time parameter into the <code>seekTo()<\/code> method.<\/p>\n<p>Our <code>src\/component\/video-player.js<\/code> should look like this.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>import React from &quot;react&quot;;\nimport ReactPlayer from &quot;react-player&quot;;\nlet player;\nconst VideoPlayer = ({\n  endVideo,\n  timePlayed,\n  controls,\n  playing,\n  videoDuration\n}) =&gt; (\n  &lt;div className=&quot;video-player&quot;&gt;\n    &lt;ReactPlayer\n      ref={(ref) =&gt; {\n        player = ref;\n      }}\n      url=&quot;https:\/\/res.cloudinary.com\/beswift-hub\/video\/upload\/v1648105334\/videoplayback_rz8dhu.mp4&quot;\n      playing={playing}\n      controls={controls}\n      onStart={() =&gt; {\n        videoDuration(player.getDuration);\n        player.seekTo(timePlayed);\n      }}\n      onEnded={endVideo}\n    \/&gt;\n  &lt;\/div&gt;\n);\nexport const MemoizedVideoPlayer = React.memo(VideoPlayer);\n<\/code><\/pre>\n<h2>Creating the video overlay<\/h2>\n<p>To make our overlay, we create a <code>video-overlay.js<\/code> file in the <code>src\/component<\/code> folder.<\/p>\n<p>For our overlay, we use a <code>div<\/code> element, a header, and a <code>p<\/code> tag.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>import React from &quot;react&quot;;\nconst VideoOverlay = () =&gt; (\n  &lt;div className=&quot;video-overlay&quot;&gt;\n    &lt;h2&gt;Live Event has finished&lt;\/h2&gt;\n    &lt;p&gt;Click the button below to restart simulation&lt;\/p&gt;\n  &lt;\/div&gt;\n);\nexport default VideoOverlay;\n<\/code><\/pre>\n<p>With that, we have been able to stimulate streaming events.<\/p>\n<p>Here\u2019s what it looks like:<\/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_sanity\/ded82207d4cbea5e89f6c0cc6dfee58999cb5a6b-709x324.png\" alt=\"image\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"709\" height=\"324\"\/><\/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_sanity\/54f1d24ce46912fdd9e54cc7463976998e94b37c-1055x700.png\" alt=\"image\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1055\" height=\"700\"\/><\/p>\n<h2>Conclusion<\/h2>\n<p>In this article, we demonstrated how to create a video player and handle player events with React Player using Gatsby.js. We specified what should happen when a user starts watching the \u2018live video\u2019 and how to place them at a specific point of the video depending on how much time has passed since the video began.<\/p>\n<p>Try to modify the video\u2019s start time relative to your current time and see how far into the live video you get!<\/p>\n<h2>Resources<\/h2>\n<p>You may also find these resources helpful.<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/www.gatsbyjs.com\/docs\/\">Gastby.js<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.npmjs.com\/package\/react-player\">React Player<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.w3schools.com\/js\/js_date_methods.asp\">JavaScript Get Date Methods<\/a>.<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":27875,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[378,134,177,382,246,393,371],"class_list":["post-27874","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-gatsbyjs","tag-guest-post","tag-javascript","tag-player-video","tag-react","tag-streaming","tag-under-review"],"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>Simulate a Streaming Event in Gatsby.js<\/title>\n<meta name=\"description\" content=\"This post demonstrated how to create a video player and handle player events with React Player using Gatsby.js.\" \/>\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\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Simulate a Streaming Event in Gatsby.js\" \/>\n<meta property=\"og:description\" content=\"This post demonstrated how to create a video player and handle player events with React Player using Gatsby.js.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-04-06T02:28:16+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"3800\" \/>\n\t<meta property=\"og:image:height\" content=\"3040\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\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\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Simulate a Streaming Event in Gatsby.js\",\"datePublished\":\"2022-04-06T02:28:16+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/\"},\"wordCount\":7,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA\",\"keywords\":[\"GatsbyJS\",\"Guest Post\",\"Javascript\",\"Player Video\",\"React\",\"Streaming\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/\",\"name\":\"Simulate a Streaming Event in Gatsby.js\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA\",\"datePublished\":\"2022-04-06T02:28:16+00:00\",\"description\":\"This post demonstrated how to create a video player and handle player events with React Player using Gatsby.js.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA\",\"width\":3800,\"height\":3040},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Simulate a Streaming Event in Gatsby.js\"}]},{\"@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\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Simulate a Streaming Event in Gatsby.js","description":"This post demonstrated how to create a video player and handle player events with React Player using Gatsby.js.","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\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/","og_locale":"en_US","og_type":"article","og_title":"Simulate a Streaming Event in Gatsby.js","og_description":"This post demonstrated how to create a video player and handle player events with React Player using Gatsby.js.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-04-06T02:28:16+00:00","og_image":[{"width":3800,"height":3040,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/"},"author":{"name":"","@id":""},"headline":"Simulate a Streaming Event in Gatsby.js","datePublished":"2022-04-06T02:28:16+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/"},"wordCount":7,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA","keywords":["GatsbyJS","Guest Post","Javascript","Player Video","React","Streaming","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/","name":"Simulate a Streaming Event in Gatsby.js","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA","datePublished":"2022-04-06T02:28:16+00:00","description":"This post demonstrated how to create a video player and handle player events with React Player using Gatsby.js.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA","width":3800,"height":3040},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-a-responsive-image-grid-with-chakra-ui-in-nextjs\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Simulate a Streaming Event in Gatsby.js"}]},{"@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":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926028\/Web_Assets\/blog\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58\/0c85803a10c8f2177a477f3d91db8af698088782-3800x3040-1_278750eb58.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27874","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\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=27874"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27874\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/27875"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=27874"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=27874"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=27874"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}