{"id":28034,"date":"2021-04-14T17:30:31","date_gmt":"2021-04-14T17:30:31","guid":{"rendered":"http:\/\/Dynamic-Video-blogs-with-Gatsby.js-and-MDX"},"modified":"2021-04-14T17:30:31","modified_gmt":"2021-04-14T17:30:31","slug":"dynamic-video-blogs-with-gatsby-js-and-mdx","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/","title":{"rendered":"Dynamic Video blogs with Gatsby.js and MDX"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>If a picture is worth a thousand words, how about a video? Mixed content presentations currently have a higher conversion rate on desired outcomes when sharing information. Hence the combined use of video, image, and text content in blogs, tutorials, and even content marketing pages.<\/p>\n<p>This post will explore adding interactive video content in a blog created with Gatsby.js and Markdown. We\u2019ll utilize MDX, which helps us render React.js components in Markdown documents.<\/p>\n<p>Markdown is a widely accepted choice for creating content, mainly blog posts, due to its simplicity in creating rich text and its closeness to writing plain text.<\/p>\n<p>At the end of this tutorial, we\u2019ll be able to display a video player playing a specific video for each of our blog posts in a Gatsby-powered blog, with text content written in Markdown.<\/p>\n<h2>Sandbox<\/h2>\n<p>We completed this project in <a href=\"https:\/\/codesandbox.io\/s\/gatsby-mdx-video-mj3-tm5z2?file=\/src\/blog-posts\/second.mdx\">CodeSandbox<\/a>. You can fork it to run the code.<\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/gatsby-mdx-video-mj3-tm5z2?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=\"Dynamic video blogs with Gatsby.js and MDX\"\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 \"><h2>Prerequisites<\/h2>\n<p>For this project, you require knowledge of JavaScript and React.js. The comprehension of Gatsby.js and Markdown content creation would be nice to have but isn\u2019t needed.<\/p>\n<p>As this is an enhancement to an existing Gatsby blog, you can utilize your current Gatsby blog or create a new one using the <a href=\"https:\/\/www.gatsbyjs.com\/starters\/gatsbyjs\/gatsby-starter-blog\">Gatsby blog starter<\/a>. We bootstrapped this project using a blog starter built with Gatsby and styled with Chakra-ui components. You can find the starter CodeSandbox below to get started quickly.<\/p>\n<p><a href=\"https:\/\/codesandbox.io\/s\/goofy-lalande-m1sh0?file=\/src\/pages\/index.js\">https:\/\/codesandbox.io\/s\/goofy-lalande-m1sh0?file=\/src\/pages\/index.js<\/a><\/p>\n<p>In the above CodeSandbox, we have the following:<\/p>\n<ul>\n<li>Installed dependencies and plugins including <a href=\"https:\/\/chakra-ui.com\/\">Chakra-ui<\/a> and <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-transformer-remark\/\">gatsby-transformer-remark<\/a>\n<\/li>\n<li>Plugin configuration in the gatsby-config.js file<\/li>\n<li>Gatsby blog setup with pages automatically created from Markdown documents<\/li>\n<li>Site layout and pages styled with Chakra-ui<\/li>\n<li>Blog posts fetched and listed on the homepage<\/li>\n<li>Single pages rendered and styled for each blog post<\/li>\n<\/ul>\n<p>We\u2019ll make several modifications to each of the above entities along the way and explain the reason for each change.<\/p>\n<p>The current home page looks like this:<\/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_207A7C74D8954454A661EB0C7D74575FD2F14F8C3F07E30D699F3383597174AA_1616000782793_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1113\" height=\"778\"\/><\/p>\n<p>Each blog post page looks like this:<\/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_207A7C74D8954454A661EB0C7D74575FD2F14F8C3F07E30D699F3383597174AA_1616000806458_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1113\" height=\"767\"\/><\/p>\n<p>Our goal is to display a video at the top of each blog post content while maintaining the markdown content.<\/p>\n<h2>Dependencies installation and plugin configuration<\/h2>\n<p>We require the MDX plugin for Gatsby and its dependencies. We\u2019ll install those using yarn with:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\">    <span class=\"hljs-selector-tag\">yarn<\/span> <span class=\"hljs-selector-tag\">add<\/span> <span class=\"hljs-selector-tag\">gatsby-plugin-mdx<\/span> <span class=\"hljs-keyword\">@mdx-js<\/span>\/mdx @mdx-js\/react\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Alternatively, you can install them with NPM using:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\">    <span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">--save<\/span> <span class=\"hljs-selector-tag\">gatsby-plugin-mdx<\/span> <span class=\"hljs-keyword\">@mdx-js<\/span>\/mdx @mdx-js\/react\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Next, we need to configure the plugin in the gatsby-config.js file in the project\u2019s root directory.\nFirst, we updated the <code>siteMetadata<\/code> object to reflect the new site title and description.<\/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-built_in\">module<\/span>.exports = {\n      <span class=\"hljs-attr\">siteMetadata<\/span>: {\n        <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">`Gatsby x MDX Video Blog`<\/span>,\n        <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">`A Gatsby video blog using MDX`<\/span>,\n        <span class=\"hljs-attr\">author<\/span>: <span class=\"hljs-string\">`@gatsbyjs`<\/span>,\n      },\n      <span class=\"hljs-attr\">plugins<\/span>: &#91;\n        <span class=\"hljs-comment\">\/\/ plugins go in here<\/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>Next, we add the plugin configurations for gatsby-plugin-mdx.<\/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-built_in\">module<\/span>.exports = {\n      <span class=\"hljs-attr\">siteMetadata<\/span>: {\n        <span class=\"hljs-comment\">\/\/ site metadata goes in here<\/span>\n      },\n      <span class=\"hljs-attr\">plugins<\/span>: &#91;\n        {\n          <span class=\"hljs-attr\">resolve<\/span>: <span class=\"hljs-string\">`gatsby-source-filesystem`<\/span>,\n          <span class=\"hljs-attr\">options<\/span>: {\n            <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">`blogPosts`<\/span>,\n            <span class=\"hljs-attr\">path<\/span>: <span class=\"hljs-string\">`<span class=\"hljs-subst\">${__dirname}<\/span>\/src\/blog-posts`<\/span>\n          }\n        },\n        {\n          <span class=\"hljs-attr\">resolve<\/span>: <span class=\"hljs-string\">`gatsby-plugin-mdx`<\/span>,\n          <span class=\"hljs-attr\">options<\/span>: {\n            <span class=\"hljs-attr\">defaultLayouts<\/span>: {\n              <span class=\"hljs-attr\">blogPosts<\/span>: <span class=\"hljs-built_in\">require<\/span>.resolve(<span class=\"hljs-string\">\".\/src\/components\/layout.js\"<\/span>),\n              <span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-built_in\">require<\/span>.resolve(<span class=\"hljs-string\">\".\/src\/components\/layout.js\"<\/span>),\n            },\n            <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">`.mdx`<\/span>, <span class=\"hljs-string\">`.md`<\/span>],\n          }\n        },\n        <span class=\"hljs-comment\">\/\/ other plugins go in here<\/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 above snippet, we sourced all blog posts using <code>gatsby-source-filesystem<\/code>, which creates file nodes for all the documents in the blog-posts directory specified.\nWe then configured <code>gatsby-plugin-mdx<\/code> and added options for a layout and default layout.\nThis layout specified is a default exported React component applied to all MDX documents (we use the current page layout created in <em>src\/components<\/em>).\nWe added default layouts for files sourced by <code>gatsby-source-filesystem<\/code> with the name of <code>blogPosts<\/code>. We set a global default layout as a fallback.<\/p>\n<p>Lastly, we added both .mdx and .md extensions so that the MDX plugin will handle both markdown and MDX files.<\/p>\n<blockquote>\n<p>With the .md and .mdx extension specified in the plugin configuration, you can discard the <code>gatsby-transformer-remark<\/code> plugin.<\/p>\n<\/blockquote>\n<h2>Page creation for MDX documents<\/h2>\n<p>With the complete MDX plugin setup in gatsby-config.js, file nodes for the MDX data are created in Gatsby\u2019s data layer and queried using GraphQL. We\u2019ll update the logic that automatically makes pages for markdown files to use MDX files instead.<\/p>\n<p>We update the automatic slug and page creation process in gatsby-node.js. First, in the <code>onCreateNode<\/code>  function, we specify the slug field\u2019s creation only for MDX node types.<\/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\">const<\/span> { createFilePath } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">`gatsby-source-filesystem`<\/span>)\n    <span class=\"hljs-keyword\">const<\/span> path = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"path\"<\/span>)\n    \n    exports.onCreateNode = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ node, getNode, actions }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> { createNodeField } = actions\n      <span class=\"hljs-keyword\">if<\/span> (node.internal.type === <span class=\"hljs-string\">\"Mdx\"<\/span>) {\n        <span class=\"hljs-keyword\">const<\/span> slug = createFilePath({ node, getNode, <span class=\"hljs-attr\">basePath<\/span>: <span class=\"hljs-string\">`blog`<\/span> })\n        createNodeField({\n          node,\n          <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">`slug`<\/span>,\n          <span class=\"hljs-attr\">value<\/span>: slug\n        })\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>This snippet creates a new slug field for all MDX file nodes using the blog post\u2019s file path.\nNext, we\u2019ll update the <code>createPages<\/code> function to create pages using each blog post\u2019s newly generated slug.<\/p>\n<blockquote>\n<p>You can create blog posts pages based on any existing field in the node. It doesn\u2019t have to be from the slug. It could be a unique ID.<\/p>\n<\/blockquote>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">const<\/span> path = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"path\"<\/span>)\n    exports.createPages = <span class=\"hljs-keyword\">async<\/span> ({ graphql, actions }) =&gt; {\n      <span class=\"hljs-keyword\">const<\/span> { createPage } = actions\n      <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> graphql(<span class=\"hljs-string\">`\n        query {\n          allMdx {\n            edges {\n              node{\n                fields {\n                  slug\n                }\n                frontmatter {\n                title\n              }\n              }\n            }\n          }\n        }\n      `<\/span>)\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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 made a GraphQL query to fetch all MDX documents with their slug and title.\nWith these, we loop through the returned data and create pages using the <code>createPage<\/code> function. We use the slug as the path, added a template for the page in the <code>component<\/code> key, and added data sent to the page as context.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    result.data.allMdx.edges.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">{node}<\/span>) =&gt;<\/span> {\n        createPage({\n          <span class=\"hljs-attr\">path<\/span>: node.fields.slug,\n          <span class=\"hljs-attr\">component<\/span>: path.resolve(<span class=\"hljs-string\">\".\/src\/templates\/blog-template-mdx.js\"<\/span>),\n          <span class=\"hljs-attr\">context<\/span>: {\n            <span class=\"hljs-attr\">slug<\/span>: node.fields.slug,\n            <span class=\"hljs-attr\">title<\/span>: node.frontmatter.title\n          }\n        })\n      })\n    }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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>We added a template for the MDX blog posts so we need to create this template in src\/templates. We make a new file called blog-template-mdx.js, similar to the blog-template.js file, in the same directory.<\/p>\n<p>In blog-template-mdx.js, we import all required dependencies and make a GraphQL query for MDX data using its slug. This slug is already passed as context data on page creation.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" 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> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { graphql } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"gatsby\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> Layout <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/layout\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> SEO <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/seo\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { Box, Center, HStack, Text } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@chakra-ui\/react\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { MDXRenderer } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"gatsby-plugin-mdx\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">\".\/blog-template.css\"<\/span>\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> query = graphql<span class=\"hljs-string\">`\n      query($slug: String!) {\n        mdx(fields: {slug: {eq: $slug}}) {\n          frontmatter {\n            publishBy\n            title\n          }\n          timeToRead\n          wordCount {\n            words\n          }\n          body\n        }\n      }\n    `<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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 we imported the <code>MDXRenderer<\/code> component, which we\u2019ll use to render the page\u2019s MDX content.  We also imported an existing CSS file to style the blog content. Also, we queried all relevant data required to display the blog content.<\/p>\n<p>Next, we create the blog post component and render all relevant data.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ Imports go here<\/span>\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> query = graphql<span class=\"hljs-string\">`\n      \/\/ graphql query goes in here\n    `<\/span>\n    <span class=\"hljs-keyword\">const<\/span> BlogPostMdx = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ data }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> post = data.mdx\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Layout<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SEO<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Home\"<\/span> \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Text<\/span> <span class=\"hljs-attr\">my<\/span>=<span class=\"hljs-string\">{5}<\/span> <span class=\"hljs-attr\">fontSize<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">3xl<\/span>\"} <span class=\"hljs-attr\">fontWeight<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">bold<\/span>\"}&gt;<\/span>\n            {post.frontmatter.title}\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Text<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Box<\/span> <span class=\"hljs-attr\">mb<\/span>=<span class=\"hljs-string\">{10}<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Center<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">HStack<\/span> <span class=\"hljs-attr\">spacing<\/span>=<span class=\"hljs-string\">{5}<\/span> <span class=\"hljs-attr\">mt<\/span>=<span class=\"hljs-string\">{3}<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Text<\/span> <span class=\"hljs-attr\">color<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">gray.400<\/span>\"} <span class=\"hljs-attr\">fontSize<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">xs<\/span>\"}&gt;<\/span>\n                  {post.timeToRead} {post.timeToRead &gt; 1 ? \"mins read\" : \"min read\"}\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Text<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Text<\/span> <span class=\"hljs-attr\">color<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">gray.400<\/span>\"} <span class=\"hljs-attr\">fontSize<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">xs<\/span>\"}&gt;<\/span>\n                  {post.frontmatter.publishBy}\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Text<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">HStack<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Center<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Box<\/span>&gt;<\/span>\n          {\/*handle MDX content*\/}\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Box<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>'<span class=\"hljs-attr\">blog-content<\/span>'}&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">MDXRenderer<\/span>&gt;<\/span>{post.body}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">MDXRenderer<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Box<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Layout<\/span>&gt;<\/span><\/span>\n      )\n    }\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> BlogPostMdx\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>We use Chakra-ui components and props to style the page template. Be sure to delete the old blog-template.js file as it will be compiled on app build and throw errors if kept.<\/p>\n<p>At this point, the hot-reloaded app should throw multiple errors as we haven\u2019t created MDX content or rebuilt the app. We update all markdown content in src\/blog-posts to MDX files by changing their file extension to .mdx.<\/p>\n<p>Next, we\u2019ll update the home page component in src\/pages\/index.js to use a new GraphQL query with MDX data.<\/p>\n<p>First, we update the imports and GraphQL query for MDX data to:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" 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> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { graphql, Link } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"gatsby\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> Layout <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/layout\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> SEO <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/seo\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { Box, Text, SimpleGrid, HStack } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@chakra-ui\/react\"<\/span>\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> query = graphql<span class=\"hljs-string\">`\n      query {\n        allMdx(sort: { order: DESC, fields: frontmatter___publishBy }) {\n          edges {\n            node {\n              fields {\n                slug\n              }\n              frontmatter {\n                title\n                publishBy\n              }\n              timeToRead\n            }\n          }\n        }\n      }\n    `<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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>Lastly, we update the <code>IndexPage<\/code> component to render the queried MDX data:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">const<\/span> IndexPage = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ data }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> blogPosts = data.allMdx.edges\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Layout<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SEO<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Home\"<\/span> \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Text<\/span> <span class=\"hljs-attr\">my<\/span>=<span class=\"hljs-string\">{5}<\/span> <span class=\"hljs-attr\">fontSize<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">3xl<\/span>\"} <span class=\"hljs-attr\">fontWeight<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">bold<\/span>\"}&gt;<\/span>\n            Blog Posts\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Text<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SimpleGrid<\/span> <span class=\"hljs-attr\">columns<\/span>=<span class=\"hljs-string\">{3}<\/span> <span class=\"hljs-attr\">spacing<\/span>=<span class=\"hljs-string\">{8}<\/span>&gt;<\/span>\n            {blogPosts &amp;&amp;\n              blogPosts.map(({ node }, index) =&gt; (\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Box<\/span> <span class=\"hljs-attr\">as<\/span>=<span class=\"hljs-string\">{Link}<\/span> <span class=\"hljs-attr\">shadow<\/span>=<span class=\"hljs-string\">\"md\"<\/span> <span class=\"hljs-attr\">borderWidth<\/span>=<span class=\"hljs-string\">\"1px\"<\/span> <span class=\"hljs-attr\">rounded<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">lg<\/span>\"} <span class=\"hljs-attr\">p<\/span>=<span class=\"hljs-string\">{2}<\/span> \n                  <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{index}<\/span>\n                  <span class=\"hljs-attr\">to<\/span>=<span class=\"hljs-string\">{node.fields.slug}<\/span>\n                &gt;<\/span>\n                  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Text<\/span> <span class=\"hljs-attr\">fontSize<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">sm<\/span>\"} <span class=\"hljs-attr\">mt<\/span>=<span class=\"hljs-string\">{3}<\/span> <span class=\"hljs-attr\">fontWeight<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">500<\/span>\"}&gt;<\/span>\n                    {node.frontmatter.title}\n                  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Text<\/span>&gt;<\/span>\n                  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">HStack<\/span> <span class=\"hljs-attr\">spcacing<\/span>=<span class=\"hljs-string\">{5}<\/span> <span class=\"hljs-attr\">mt<\/span>=<span class=\"hljs-string\">{1}<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Text<\/span> <span class=\"hljs-attr\">color<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">gray.400<\/span>\"} <span class=\"hljs-attr\">fontSize<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">xs<\/span>\"}&gt;<\/span>\n                      {node.timeToRead} {node.timeToRead &gt; 1 ? \"mins\" : \"min\"}\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Text<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Text<\/span> <span class=\"hljs-attr\">color<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">gray.400<\/span>\"} <span class=\"hljs-attr\">fontSize<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">xs<\/span>\"}&gt;<\/span>\n                      {node.frontmatter.publishBy}\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Text<\/span>&gt;<\/span>\n                  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">HStack<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Box<\/span>&gt;<\/span>\n              ))}\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">SimpleGrid<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Layout<\/span>&gt;<\/span><\/span>\n      )\n    }\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> IndexPage\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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>With these, we\u2019ll restart the development server to see the blog with MDX content.<\/p>\n<h2>Video player component creation<\/h2>\n<p>We\u2019ll utilize the <a href=\"https:\/\/cloudinary.com\/documentation\/react_integration\">Cloudinary-React SDK<\/a> to display a video player with a remote video on Cloudinary. To use media assets on Cloudinary for this project, you need to have a Cloudinary account with videos uploaded to Cloudinary. <a href=\"https:\/\/cloudinary.com\/signup\">You create one here<\/a>.<\/p>\n<p>We proceed to install the cloudinary-react SDK using yarn with<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    yarn add cloudinary-react  \n<\/code><\/span><\/pre>\n<p>The cloudinary-react package exports Image, Video, and Transformation React components required to present media assets and optimized transformations from Cloudinary.<\/p>\n<blockquote>\n<p>While we use Cloudinary here, you can use any video library  or even the native. <code>&lt;video&gt;<\/code> element in HTML to render your choice video component.<\/p>\n<\/blockquote>\n<p>We proceed to create a component in src\/components called video-player.js.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ src\/components\/video-player.js<\/span>\n    <span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { Box, Center } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@chakra-ui\/react\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { CloudinaryContext, Video } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"cloudinary-react\"<\/span>\n    \n    <span class=\"hljs-keyword\">const<\/span> VideoPlayer = <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\">Box<\/span> <span class=\"hljs-attr\">mb<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">4<\/span>%\"}&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Center<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CloudinaryContext<\/span> <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">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\">autoPlay<\/span> <span class=\"hljs-attr\">controls<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">CloudinaryContext<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Center<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Box<\/span>&gt;<\/span><\/span>\n      )\n    }\n    \n    <span class=\"hljs-keyword\">export<\/span> { VideoPlayer }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><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 functional component receives a prop of <code>publicId<\/code> of the Cloudinary video. It renders a video player with autoplay content and playback controls.<\/p>\n<p><code>CloudinaryContext<\/code> specifies data required and available to all child Cloudinary components, i.e., cloudName. Like all other components of this project, we use Chakra-ui for styling.<\/p>\n<p>We can further enhance this video component to use video transformations and optimizations available on Cloudinary.<\/p>\n<p>We can now import this component into any MDX document and render the video on runtime. To skip this VideoPlayer component\u2019s importation into every MDX document, we will utilize shortcodes to import the component into each MDX document automatically.<\/p>\n<p>We handle the shortcode in the MDX document template. First, we import the VideoPlayer and MDXProvider components.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ src\/components\/layout.js<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { VideoPlayer } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/video-player\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { MDXProvider } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@mdx-js\/react\"<\/span>\n    \n    <span class=\"hljs-keyword\">const<\/span> shortcodeComponents = { VideoPlayer }\n    <span class=\"hljs-keyword\">const<\/span> Layout = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ children }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> data = useStaticQuery(graphql<span class=\"hljs-string\">`\n        \/\/ GraphQL query goes in here\n      `<\/span>)\n    \n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Header<\/span> <span class=\"hljs-attr\">siteTitle<\/span>=<span class=\"hljs-string\">{data.site.siteMetadata?.title<\/span> || `<span class=\"hljs-attr\">Title<\/span>`} \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Box<\/span> <span class=\"hljs-attr\">margin<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">0<\/span> <span class=\"hljs-attr\">auto<\/span>\"} <span class=\"hljs-attr\">maxWidth<\/span>=<span class=\"hljs-string\">{960}<\/span> <span class=\"hljs-attr\">padding<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">0<\/span> <span class=\"hljs-attr\">1.0875rem<\/span> <span class=\"hljs-attr\">1.45rem<\/span>`}&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Box<\/span>&gt;<\/span>\n              {\/*Furnish the provider with shortcode *\/}\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">MDXProvider<\/span> <span class=\"hljs-attr\">components<\/span>=<span class=\"hljs-string\">{shortcodeComponents}<\/span>&gt;<\/span>{children}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">MDXProvider<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Box<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Box<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/&gt;<\/span><\/span>\n      )\n    }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><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>Any component specified in the <code>shortcodeComponents<\/code> object is available to all MDX documents with the above layout.<\/p>\n<p>Finally, we\u2019ll add the <code>VideoPlayer<\/code> component with a <code>publicId<\/code> of an existing video on Cloudinary to each MDX document.<\/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_207A7C74D8954454A661EB0C7D74575FD2F14F8C3F07E30D699F3383597174AA_1616008029840_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1033\" height=\"632\"\/><\/p>\n<p>We restart the development server to rebuild the site. Here\u2019s an example blog post with a video of chickens clucking away:<\/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_207A7C74D8954454A661EB0C7D74575FD2F14F8C3F07E30D699F3383597174AA_1616009393685_media-jam3-video.gif\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"480\" height=\"614\"\/><\/p>\n<h2>Conclusion<\/h2>\n<p>In this post, we discussed how to create an interactive blog using MDX and Gatsby.js. We also introduced a video player with which we added remote videos from Cloudinary.\nYou can add other interactive components to the blog using MDX. Also, add video transformations to each video or customize the video player even further to have subtitles and a caption.<\/p>\n<p>You may find the following resources useful:<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/video_manipulation_and_delivery\">Cloudinary video transformations<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-plugin-mdx\/\">Gatsby-plugin-mdx<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/chakra-ui.com\/docs\/getting-started\">Chakra-ui documentation<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28035,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[378,134,382,246,371,303],"class_list":["post-28034","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-gatsbyjs","tag-guest-post","tag-player-video","tag-react","tag-under-review","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>Dynamic Video blogs with Gatsby.js and MDX<\/title>\n<meta name=\"description\" content=\"Learn to create a blog with Gatsby.js and interactive content including Video with MDX\" \/>\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\/dynamic-video-blogs-with-gatsby-js-and-mdx\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Dynamic Video blogs with Gatsby.js and MDX\" \/>\n<meta property=\"og:description\" content=\"Learn to create a blog with Gatsby.js and interactive content including Video with MDX\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-04-14T17:30:31+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Dynamic Video blogs with Gatsby.js and MDX\",\"datePublished\":\"2021-04-14T17:30:31+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/\"},\"wordCount\":8,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA\",\"keywords\":[\"GatsbyJS\",\"Guest Post\",\"Player Video\",\"React\",\"Under Review\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2021\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/\",\"name\":\"Dynamic Video blogs with Gatsby.js and MDX\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA\",\"datePublished\":\"2021-04-14T17:30:31+00:00\",\"description\":\"Learn to create a blog with Gatsby.js and interactive content including Video with MDX\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA\",\"width\":5398,\"height\":3648},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Dynamic Video blogs with Gatsby.js and MDX\"}]},{\"@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":"Dynamic Video blogs with Gatsby.js and MDX","description":"Learn to create a blog with Gatsby.js and interactive content including Video with MDX","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\/dynamic-video-blogs-with-gatsby-js-and-mdx\/","og_locale":"en_US","og_type":"article","og_title":"Dynamic Video blogs with Gatsby.js and MDX","og_description":"Learn to create a blog with Gatsby.js and interactive content including Video with MDX","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/","og_site_name":"Cloudinary Blog","article_published_time":"2021-04-14T17:30:31+00:00","twitter_card":"summary_large_image","twitter_image":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/"},"author":{"name":"","@id":""},"headline":"Dynamic Video blogs with Gatsby.js and MDX","datePublished":"2021-04-14T17:30:31+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/"},"wordCount":8,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA","keywords":["GatsbyJS","Guest Post","Player Video","React","Under Review","Video"],"inLanguage":"en-US","copyrightYear":"2021","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/","name":"Dynamic Video blogs with Gatsby.js and MDX","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA","datePublished":"2021-04-14T17:30:31+00:00","description":"Learn to create a blog with Gatsby.js and interactive content including Video with MDX","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA","width":5398,"height":3648},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/dynamic-video-blogs-with-gatsby-js-and-mdx\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Dynamic Video blogs with Gatsby.js and MDX"}]},{"@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\/v1681925620\/Web_Assets\/blog\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104\/8dd450988b9e5d0ab9b5036c5910d39123dc1726-5398x3648-1_2803564104.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28034","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=28034"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28034\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28035"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28034"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28034"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28034"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}