{"id":454,"date":"2023-10-09T11:00:00","date_gmt":"2023-10-09T11:00:00","guid":{"rendered":"http:\/\/sobre-portugal.com\/?p=454"},"modified":"2024-06-12T20:27:22","modified_gmt":"2024-06-12T20:27:22","slug":"gatsby-headaches-working-with-media-part-1","status":"publish","type":"post","link":"http:\/\/sobre-portugal.com\/index.php\/2023\/10\/09\/gatsby-headaches-working-with-media-part-1\/","title":{"rendered":"Gatsby Headaches: Working With Media (Part 1)"},"content":{"rendered":"

Gatsby Headaches: Working With Media (Part 1)<\/title><\/p>\n<article>\n<header>\n<h1>Gatsby Headaches: Working With Media (Part 1)<\/h1>\n<address>Juan Diego Rodr\u00edguez<\/address>\n<p> 2023-10-09T11:00:00+00:00<br \/>\n 2024-06-12T20:05:40+00:00<br \/>\n <\/header>\n<p>Working with media files in Gatsby might not be as straightforward as expected. I remember starting my first Gatsby project. After consulting Gatsby\u2019s documentation, I discovered I needed to use the <code>gatsby-source-filesystem<\/code> plugin to make queries for local files. Easy enough!<\/p>\n<p>That\u2019s where things started getting complicated. Need to use images? Check the docs and install one — or more! — of the many, many plugins available for handling images. How about working with SVG files? There is another plugin for that. Video files? You get the idea.<\/p>\n<p>It\u2019s all great until any of those plugins or packages become outdated and go unmaintained. That\u2019s where the headaches start.<\/p>\n<p>If you are unfamiliar with <a href=\"https:\/\/www.gatsbyjs.com\">Gatsby<\/a>, it\u2019s a React-based static site generator that uses GraphQL to pull structured data from various sources and uses webpack to bundle a project so it can then be deployed and served as static files. It\u2019s essentially a static site generator with reactivity that can pull data from a vast array of sources.<\/p>\n<p>Like many static site frameworks in the Jamstack, Gatsby has traditionally enjoyed a great reputation as a <a href=\"https:\/\/www.gatsbyjs.com\/blog\/comparing-website-performance-gatsby-vs-next-vs-nuxt\/\">performant framework<\/a>, although <a href=\"https:\/\/2022.stateofjs.com\/en-US\/libraries\/\">it has taken a hit in recent years<\/a>. Based on what I\u2019ve seen, however, it\u2019s not so much that the framework is fast or slow but how the framework is configured to handle many of the sorts of things that impact performance, including media files.<\/p>\n<p>So, let\u2019s solve the headaches you might encounter when working with media files in a Gatsby project. This article is the first of a brief two-part series where we will look specifically at the media you are most likely to use: images, video, and audio. After that, the <a href=\"https:\/\/www.smashingmagazine.com\/2023\/10\/gatsby-headaches-working-media-part2\/\">second part of this series<\/a> will get into different types of files, including Markdown, PDFs, and even 3D models.<\/p>\n<h2 id=\"solving-image-headaches-in-gatsby\">Solving Image Headaches In Gatsby<\/h2>\n<p>I think that the process of optimizing images can fall into four different buckets:<\/p>\n<ol>\n<li><strong>Optimize image files.<\/strong><br \/>\nMinimizing an image\u2019s file size without losing quality directly leads to shorter fetching times. This can be done manually or during a build process. It\u2019s also possible to use a service, like Cloudinary, to handle the work on demand.<\/li>\n<li><strong>Prioritize images that are part of the First Contentful Paint (FCP).<\/strong><br \/>\nFCP is a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/First_contentful_paint\">metric that measures the time<\/a> between the point when a page starts loading to when the first bytes of content are rendered. The idea is that fetching assets that are part of that initial render earlier results in faster loading rather than waiting for other assets lower on the chain.<\/li>\n<li><strong>Lazy loading other images.<\/strong><br \/>\nWe can prevent the rest of the images from render-blocking other assets using the <code>loading="lazy"<\/code> attribute on images.<\/li>\n<li><strong>Load the right image file for the right context.<\/strong><br \/>\nWith <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/HTML\/Multimedia_and_embedding\/Responsive_images\">responsive images<\/a>, we can serve one version of an image file at one screen size and serve another image at a different screen size with the <code>srcset<\/code> and <code>sizes<\/code> attributes or with the <code><picture><\/code> element.<\/li>\n<\/ol>\n<p>These are great principles for any website, not only those built with Gatsby. But how we build them into a Gatsby-powered site can be confusing, which is why I\u2019m writing this article and perhaps why you\u2019re reading it.<\/p>\n<h3 id=\"lazy-loading-images-in-gatsby\">Lazy Loading Images In Gatsby<\/h3>\n<p>We can apply an image to a React component in a Gatsby site like this:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport forest from \".\/assets\/images\/forest.jpg\";\n\nconst ImageHTML = () => {\n return <img src={ forest } alt=\"Forest trail\" \/>;\n};\n<\/code><\/pre>\n<p>It\u2019s important to <code>import<\/code> the image as a JavaScript module. This lets webpack know to bundle the image and generate a path to its location in the public folder.<\/p>\n<p>This works fine, but when are we ever working with only one image? What if we want to make an image gallery that contains 100 images? If we try to load that many <code><img><\/code> tags at once, they will certainly slow things down and could affect the FCP. That\u2019s where the third principle that uses the <code>loading="lazy"<\/code> attribute can come into play.<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport forest from \".\/assets\/images\/forest.jpg\";\n\nconst LazyImageHTML = () => {\n return <img src={ forest } loading=\"lazy\" alt=\"Forest trail\" \/>;\n};\n<\/code><\/pre>\n<p>We can do the opposite with <code>loading="eager"<\/code>. It instructs the browser to load the image as soon as possible, regardless of whether it is onscreen or not.<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport forest from \".\/assets\/images\/forest.jpg\";\n\nconst EagerImageHTML = () => {\n return <img src={ forest } loading=\"eager\" alt=\"Forest trail\" \/>;\n};\n<\/code><\/pre>\n<h3 id=\"implementing-responsive-images-in-gatsby\">Implementing Responsive Images In Gatsby<\/h3>\n<p>This is a basic example of the HTML for responsive images:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><img\n srcset=\".\/assets\/images\/forest-400.jpg 400w, .\/assets\/images\/forest-800.jpg 800w\"\n sizes=\"(max-width: 500px) 400px, 800px\"\n alt=\"Forest trail\"\n\/>\n<\/code><\/pre>\n<\/div>\n<p>In Gatsby, we must <code>import<\/code> the images first and pass them to the <code>srcset<\/code> attribute as template literals so webpack can bundle them:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport forest800 from \".\/assets\/images\/forest-800.jpg\";\n\nimport forest400 from \".\/assets\/images\/forest-400.jpg\";\n\nconst ResponsiveImageHTML = () => {\n return (\n <img\n srcSet={`\n\n ${ forest400 } 400w,\n\n ${ forest800 } 800w\n\n `}\n sizes=\"(max-width: 500px) 400px, 800px\"\n alt=\"Forest trail\"\n \/>\n );\n};\n<\/code><\/pre>\n<p>That should take care of any responsive image headaches in the future.<\/p>\n<h3 id=\"loading-background-images-in-gatsby\">Loading Background Images In Gatsby<\/h3>\n<p>What about pulling in the URL for an image file to use on the CSS <code>background-url<\/code> property? That looks something like this:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport \".\/style.css\";\n\nconst ImageBackground = () => {\n return <div className=\"banner\"><\/div>;\n};\n<\/code><\/pre>\n<pre><code class=\"language-css\">\/* style.css *\/\n\n.banner {\n aspect-ratio: 16\/9;\n background-size: cover;\n\n background-image: url(\".\/assets\/images\/forest-800.jpg\");\n\n \/* etc. *\/\n}\n<\/code><\/pre>\n<p>This is straightforward, but there is still room for optimization! For example, we can do the CSS version of responsive images, which loads the version we want at specific breakpoints.<\/p>\n<pre><code class=\"language-css\">\/* style.css *\/\n\n@media (max-width: 500px) {\n .banner {\n background-image: url(\".\/assets\/images\/forest-400.jpg\");\n }\n}\n<\/code><\/pre>\n<h3 id=\"using-the-gatsby-source-filesystem-plugin\">Using The <code>gatsby-source-filesystem<\/code> Plugin<\/h3>\n<p>Before going any further, I think it is worth installing the <code>gatsby-source-filesystem<\/code> plugin. It\u2019s an essential part of any Gatsby project because it allows us to <a href=\"https:\/\/www.gatsbyjs.com\/docs\/how-to\/sourcing-data\/sourcing-from-the-filesystem\/\">query data from various directories in the local filesystem<\/a>, making it simpler to fetch assets, like a folder of optimized images.<\/p>\n<pre><code class=\"language-bash\">npm i gatsby-source-filesystem\n<\/code><\/pre>\n<p>We can add it to our <code>gatsby-config.js<\/code> file and specify the directory from which we will query our media assets:<\/p>\n<pre><code class=\"language-javascript\">\/\/ gatsby-config.js\n\nmodule.exports = {\n plugins: [\n {\n resolve: `gatsby-source-filesystem`,\n\n options: {\n name: `assets`,\n\n path: `${ __dirname }\/src\/assets`,\n },\n },\n ],\n};\n<\/code><\/pre>\n<p>Remember to restart your development server to see changes from the <code>gatsby-config.js<\/code> file.<\/p>\n<p>Now that we have <code>gatsby-source-filesystem<\/code> installed, we can continue solving a few other image-related headaches. For example, the next plugin we look at is capable of simplifying the cures we used for lazy loading and responsive images.<\/p>\n<h3 id=\"using-the-gatsby-plugin-image-plugin\">Using The <code>gatsby-plugin-image<\/code> Plugin<\/h3>\n<p>The <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-plugin-image\/\"><code>gatsby-plugin-image<\/code> plugin<\/a> (not to be confused with the outdated <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-image\/\"><code>gatsby-image<\/code> plugin<\/a>) uses techniques that <strong>automatically handle various aspects of image optimization<\/strong>, such as lazy loading, responsive sizing, and even generating optimized image formats for modern browsers.<\/p>\n<p>Once installed, we can replace standard <code><img><\/code> tags with either the <code><GatsbyImage><\/code> or <code><StaticImage><\/code> components, depending on the use case. These components take advantage of the plugin\u2019s features and use the <code><picture><\/code> HTML element to ensure the most appropriate image is served to each user based on their device and network conditions.<\/p>\n<p>We can start by installing <code>gatsby-plugin-image<\/code> and the other plugins it depends on:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-bash\">npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp\n<\/code><\/pre>\n<\/div>\n<p>Let\u2019s add them to the <code>gatsby-config.js<\/code> file:<\/p>\n<pre><code class=\"language-javascript\">\/\/ gatsby-config.js\n\nmodule.exports = {\nplugins: [\n\n\/\/ other plugins\n`gatsby-plugin-image`,\n`gatsby-plugin-sharp`,\n`gatsby-transformer-sharp`],\n\n};\n<\/code><\/pre>\n<p>This provides us with some features we will put to use a bit later.<\/p>\n<h3 id=\"using-the-staticimage-component\">Using The <code>StaticImage<\/code> Component<\/h3>\n<p>The <code>StaticImage<\/code> component serves <strong>images that don\u2019t require dynamic sourcing or complex transformations<\/strong>. It\u2019s particularly useful for scenarios where you have a fixed image source that doesn\u2019t change based on user interactions or content updates, like logos, icons, or other static images that remain consistent.<\/p>\n<p>The main attributes we will take into consideration are:<\/p>\n<ul>\n<li><strong><code>src<\/code>:<\/strong> This attribute is required and should be set to the path of the image you want to display.<\/li>\n<li><strong><code>alt<\/code>:<\/strong> Provides alternative text for the image.<\/li>\n<li><strong><code>placeholder<\/code>:<\/strong> This attribute can be set to either <code>blurred<\/code> or <code>dominantColor<\/code> to define the type of placeholder to display while the image is loading.<\/li>\n<li><strong><code>layout<\/code>:<\/strong> This defines how the image should be displayed. It can be set to <code>fixed<\/code> for, as you might imagine, images with a fixed size, <code>fullWidth<\/code> for images that span the entire container, and <code>constrained<\/code> for images scaled down to fit their container.<\/li>\n<li><strong><code>loading<\/code>:<\/strong> This determines when the image should start loading while also supporting the <code>eager<\/code> and <code>lazy<\/code> options.<\/li>\n<\/ul>\n<p>Using <code>StaticImage<\/code> is similar to using a regular HTML <code><img><\/code> tag. However, <code>StaticImage<\/code> requires passing the string directly to the <code>src<\/code> attribute so it can be bundled by webpack.<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport { StaticImage } from \"gatsby-plugin-image\";\n\nconst ImageStaticGatsby = () => {\n return (\n <StaticImage\n src=\".\/assets\/images\/forest.jpg\"\n placeholder=\"blurred\"\n layout=\"constrained\"\n alt=\"Forest trail\"\n loading=\"lazy\"\n \/>\n );\n };\n<\/code><\/pre>\n<p>The <code>StaticImage<\/code> component is great, but you have to take its constraints into account:<\/p>\n<ul>\n<li><strong>No Dynamically Loading URLs<\/strong><br \/>\nOne of the most significant limitations is that the <code>StaticImage<\/code> component doesn\u2019t support dynamically loading images based on URLs fetched from data sources or APIs.<\/li>\n<li><strong>Compile-Time Image Handling<\/strong><br \/>\nThe <code>StaticImage<\/code> component\u2019s image handling occurs at compile time. This means that the images you specify are processed and optimized when the Gatsby site is built. Consequently, if you have images that need to change frequently based on user interactions or updates, the static nature of this component might not fit your needs.<\/li>\n<li><strong>Limited Transformation Options<\/strong><br \/>\nUnlike the more versatile <code>GatsbyImage<\/code> component, the <code>StaticImage<\/code> component provides fewer transformation options, e.g., there is no way to apply complex transformations like cropping, resizing, or adjusting image quality directly within the component. You may want to consider alternative solutions if you require advanced transformations.<\/li>\n<\/ul>\n<h3 id=\"using-the-gatsbyimage-component\">Using The <code>GatsbyImage<\/code> Component<\/h3>\n<p>The <code>GatsbyImage<\/code> component is a <strong>more versatile solution<\/strong> that addresses the limitations of the <code>StaticImage<\/code> component. It\u2019s particularly useful for scenarios involving dynamic image loading, complex transformations, and advanced customization.<\/p>\n<p>Some ideal use cases where <code>GatsbyImage<\/code> is particularly useful include:<\/p>\n<ul>\n<li><strong>Dynamic Image Loading<\/strong><br \/>\nIf you need to load images dynamically based on data from APIs, content management systems, or other sources, the <code>GatsbyImage<\/code> component is the go-to choice. It can fetch images and optimize their loading behavior.<\/li>\n<li><strong>Complex transformations<\/strong><br \/>\nThe <code>GatsbyImage<\/code> component is well-suited for advanced transformations, using GraphQL queries to apply them.<\/li>\n<li><strong>Responsive images<\/strong><br \/>\nFor responsive design, the <code>GatsbyImage<\/code> component excels by automatically generating multiple sizes and formats of an image, ensuring that users receive an appropriate image based on their device and network conditions.<\/li>\n<\/ul>\n<p>Unlike the <code>StaticImage<\/code> component, which uses a <code>src<\/code> attribute, <code>GatsbyImage<\/code> has an <code>image<\/code> attribute that takes a <code>gatsbyImageData<\/code> object. <code>gatsbyImageData<\/code> contains the image information and can be queried from GraphQL using the following query.<\/p>\n<pre><code class=\"language-javascript\">query {\n file(name: { eq: \"forest\" }) {\n childImageSharp {\n gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)\n }\n\n name\n }\n}\n<\/code><\/pre>\n<p>If you\u2019re following along, you can look around your Gatsby data layer at <code>http:\/\/localhost:8000\/___graphql<\/code>.<\/p>\n<p>From here, we can use the <code>useStaticQuery<\/code> hook and the <code>graphql<\/code> tag to fetch data from the data layer:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport { useStaticQuery, graphql } from \"gatsby\";\n\nimport { GatsbyImage, getImage } from \"gatsby-plugin-image\";\n\nconst ImageGatsby = () => {\n \/\/ Query data here:\n\n const data = useStaticQue(graphql``);\n\n return <div><\/div>;\n};\n<\/code><\/pre>\n<p>Next, we can write the GraphQL query inside of the <code>graphql<\/code> tag:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport { useStaticQuery, graphql } from \"gatsby\";\n\nconst ImageGatsby = () => {\n const data = useStaticQuery(graphql`\n query {\n file(name: { eq: \"forest\" }) {\n childImageSharp {\n gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)\n }\n\n name\n }\n }\n `);\n\n return <div><\/div>;\n};\n<\/code><\/pre>\n<\/div>\n<p>Next, we import the <code>GatsbyImage<\/code> component from <code>gatsby-plugin-image<\/code> and assign the image\u2019s <code>gatsbyImageData<\/code> property to the <code>image<\/code> attribute:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport { useStaticQuery, graphql } from \"gatsby\";\n\nimport { GatsbyImage } from \"gatsby-plugin-image\";\n\nconst ImageGatsby = () => {\n const data = useStaticQuery(graphql`\n query {\n file(name: { eq: \"forest\" }) {\n childImageSharp {\n gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)\n }\n\n name\n }\n }\n `);\n\n return <GatsbyImage image={ data.file.childImageSharp.gatsbyImageData } alt={ data.file.name } \/>;\n};\n<\/code><\/pre>\n<\/div>\n<p>Now, we can use the <code>getImage<\/code> helper function to make the code easier to read. When given a <code>File<\/code> object, the function returns the <code>file.childImageSharp.gatsbyImageData<\/code> property, which can be passed directly to the <code>GatsbyImage<\/code> component.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport { useStaticQuery, graphql } from \"gatsby\";\n\nimport { GatsbyImage, getImage } from \"gatsby-plugin-image\";\n\nconst ImageGatsby = () => {\n const data = useStaticQuery(graphql`\n query {\n file(name: { eq: \"forest\" }) {\n childImageSharp {\n gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)\n }\n\n name\n }\n }\n `);\n\n const image = getImage(data.file);\n\n return <GatsbyImage image={ image } alt={ data.file.name } \/>;\n};\n<\/code><\/pre>\n<\/div>\n<h3 id=\"using-the-gatsby-background-image-plugin\">Using The <code>gatsby-background-image<\/code> Plugin<\/h3>\n<p>Another plugin we could use to take advantage of Gatsby\u2019s image optimization capabilities is the <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-background-image\/\"><code>gatsby-background-image<\/code> plugin<\/a>. However, I do not recommend using this plugin since it is outdated and prone to compatibility issues. Instead, <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-background-image\/#gatsby-34--gatsby-plugin-image\">Gatsby suggests<\/a> using <code>gatsby-plugin-image<\/code> when working with the latest Gatsby version 3 and above.<\/p>\n<p>If this compatibility doesn\u2019t represent a significant problem for your project, you can refer to <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-background-image\/#table-of-contents\">the plugin\u2019s documentation for specific instructions<\/a> and use it in place of the CSS <code>background-url<\/code> usage I described earlier.<\/p>\n<h2 id=\"solving-video-and-audio-headaches-in-gatsby\">Solving Video And Audio Headaches In Gatsby<\/h2>\n<p>Working with videos and audio can be a bit of a mess in Gatsby since it lacks plugins for sourcing and optimizing these types of files. In fact, <a href=\"https:\/\/www.gatsbyjs.com\/docs\/how-to\/images-and-media\/working-with-video\/#hosting-your-own-html5-video-files\">Gatsby\u2019s documentation<\/a> doesn\u2019t name or recommend any official plugins we can turn to.<\/p>\n<p>That means we will have to use <em>vanilla<\/em> methods for videos and audio in Gatsby.<\/p>\n<h3 id=\"using-the-html-video-element\">Using The HTML <code>video<\/code> Element<\/h3>\n<p>The HTML <code>video<\/code> element is capable of serving different versions of the same video using the <code><source><\/code> tag, much like the <code>img<\/code> element uses the <code>srset<\/code> attribute to do the same for responsive images.<\/p>\n<p>That allows us to not only serve a more performant video format but also to provide a fallback video for older browsers that may not support the bleeding edge:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport natureMP4 from \".\/assets\/videos\/nature.mp4\";\n\nimport natureWEBM from \".\/assets\/videos\/nature.webm\";\n\nconst VideoHTML = () => {\n return (\n <video controls>\n <source src={ natureMP4 } type=\"video\/mp4\" \/>\n\n <source src={ natureWEBM } type=\"video\/webm\" \/>\n <\/video>\n );\n};\n\nP;\n<\/code><\/pre>\n<p>We can also apply lazy loading to videos like we do for images. While videos do not support the <code>loading="lazy"<\/code> attribute, there is a <code>preload<\/code> attribute that is similar in nature. When set to <code>none<\/code>, the attribute instructs the browser to load a video and its metadata only when the user interacts with it. In other words, it\u2019s lazy-loaded until the user taps or clicks the video.<\/p>\n<p>We can also set the attribute to <code>metadata<\/code> if we want the video\u2019s details, such as its duration and file size, fetched right away.<\/p>\n<pre><code class=\"language-javascript\"><video controls preload=\"none\">\n <source src={ natureMP4 } type=\"video\/mp4\" \/>\n\n <source src={ natureWEBM } type=\"video\/webm\" \/>\n<\/video>\n<\/code><\/pre>\n<p><strong>Note<\/strong>: <em>I personally do not recommend using the <code>autoplay<\/code> attribute since it is disruptive and disregards the <code>preload<\/code> attribute, causing the video to load right away.<\/em><\/p>\n<p>And, like images, display a placeholder image for a video while it is loading with the <code>poster<\/code> attribute pointing to an image file.<\/p>\n<pre><code class=\"language-javascript\"><video controls preload=\"none\" poster={ forest }>\n <source src={ natureMP4 } type=\"video\/mp4\" \/>\n\n <source src={ natureWEBM } type=\"video\/webm\" \/>\n<\/video>\n<\/code><\/pre>\n<h3 id=\"using-the-html-audio-element\">Using The HTML <code>audio<\/code> Element<\/h3>\n<p>The <code>audio<\/code> and <code>video<\/code> elements behave similarly, so adding an <code>audio<\/code> element in Gatsby looks nearly identical, aside from the element:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport audioSampleMP3 from \".\/assets\/audio\/sample.mp3\";\n\nimport audioSampleWAV from \".\/assets\/audio\/sample.wav\";\n\nconst AudioHTML = () => {\n return (\n <audio controls>\n <source src={ audioSampleMP3 } type=\"audio\/mp3\" \/>\n\n <source src={ audioSampleWAV } type=\"audio\/wav\" \/>\n <\/audio>\n );\n};\n<\/code><\/pre>\n<p>As you might expect, the <code>audio<\/code> element also supports the <code>preload<\/code> attribute:<\/p>\n<pre><code class=\"language-javascript\"><audio controls preload=\"none\">\n <source src={ audioSampleMP3 } type=\"audio\/mp3\" \/>\n\n <source src={a udioSampleWAV } type=\"audio\/wav\" \/>\n<\/audio>\n<\/code><\/pre>\n<p>This is probably as good as we can do to use videos and images in Gatsby with performance in mind, aside from saving and compressing the files as best we can before serving them.<\/p>\n<h2 id=\"solving-iframe-headaches-in-gatsby\">Solving iFrame Headaches In Gatsby<\/h2>\n<p>Speaking of video, what about ones embedded in an <code><iframe><\/code> like we might do with a video from YouTube, Vimeo, or some other third party? Those can certainly lead to performance headaches, but it\u2019s not as we have direct control over the video file and where it is served.<\/p>\n<p>Not all is lost because the HTML <code>iframe<\/code> element supports lazy loading the same way that images do.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nconst VideoIframe = () => {\n return (\n <iframe\n src=\"https:\/\/www.youtube.com\/embed\/jNQXAC9IVRw\"\n title=\"Me at the Zoo\"\n allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n loading=\"lazy\"\n \/>\n );\n};\n<\/code><\/pre>\n<\/div>\n<p>Embedding a third-party video player via <code>iframe<\/code> can possibly be an easier path than using the HTML <code>video<\/code> element. <code>iframe<\/code> elements are cross-platform compatible and could reduce hosting demands if you are working with heavy video files on your own server.<\/p>\n<p>That said, an <code>iframe<\/code> is essentially a <strong>sandbox serving a page from an outside source<\/strong>. They\u2019re not weightless, and we have no control over the code they contain. There are also GDPR considerations when it comes to services (such as YouTube) due to cookies, data privacy, and third-party ads.<\/p>\n<h2 id=\"solving-svg-headaches-in-gatsby\">Solving SVG Headaches In Gatsby<\/h2>\n<p>SVGs contribute to improved page performance in several ways. Their vector nature results in a <strong>much smaller file size<\/strong> compared to raster images, and they <strong>can be scaled up without compromising quality<\/strong>. And SVGs can be compressed with GZIP, further reducing file sizes.<\/p>\n<p>That said, there are several ways that we can use SVG files. Let\u2019s tackle each one in the contact of Gatsby.<\/p>\n<h3 id=\"using-inline-svg\">Using Inline SVG<\/h3>\n<p>SVGs are essentially lines of code that describe shapes and paths, making them lightweight and highly customizable. Due to their XML-based structure, SVG images can be directly embedded within the HTML <code><svg><\/code> tag.<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\n\n\nconst SVGInline = () => {\n\n return (\n\n <svg viewBox=\"0 0 24 24\" fill=\"#000000\">\n\n <!-- etc. -->\n\n <\/svg>\n\n );\n\n};\n<\/code><\/pre>\n<p>Just remember to change certain SVG attributes, such as <code>xmlns:xlink<\/code> or <code>xlink:href<\/code>, to JSX attribute spelling, like <code>xmlnsXlink<\/code> and <code>xlinkHref<\/code>, respectively.<\/p>\n<h3 id=\"using-svg-in-img-elements\">Using SVG In <code>img<\/code> Elements<\/h3>\n<p>An SVG file can be passed into an <code>img<\/code> element’s <code>src<\/code> attribute like any other image file.<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport picture from \".\/assets\/svg\/picture.svg\";\n\nconst SVGinImg = () => {\n return <img src={ picture } alt=\"Picture\" \/>;\n};\n<\/code><\/pre>\n<p>Loading SVGs inline or as HTML images are the <em>de facto<\/em> approaches, but there are React and Gatsby plugins capable of simplifying the process, so let\u2019s look at those next.<\/p>\n<h3 id=\"inlining-svg-with-the-react-svg-plugin\">Inlining SVG With The <code>react-svg<\/code> Plugin<\/h3>\n<p><a href=\"https:\/\/github.com\/tanem\/react-svg\"><code>react-svg<\/code><\/a> provides an efficient way to render SVG images as React components by swapping a <code>ReactSVG<\/code> component in the DOM with an inline SVG.<\/p>\n<p>Once installing the plugin, import the <code>ReactSVG<\/code> component and assign the SVG file to the component\u2019s <code>src<\/code> attribute:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport { ReactSVG } from \"react-svg\";\n\nimport camera from \".\/assets\/svg\/camera.svg\";\n\nconst SVGReact = () => {\n return <ReactSVG src={ camera } \/>;\n};\n<\/code><\/pre>\n<h3 id=\"using-the-gatsby-plugin-react-svg-plugin\">Using The <code>gatsby-plugin-react-svg<\/code> Plugin<\/h3>\n<p>The <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-plugin-react-svg\/\"><code>gatsby-plugin-react-svg<\/code> plugin<\/a> adds <a href=\"https:\/\/github.com\/jhamlet\/svg-react-loader\">svg-react-loader<\/a> to your Gatsby project\u2019s webpack configuration. The plugin <strong>adds a loader to support using SVG files as React components while bundling them as inline SVG<\/strong>.<\/p>\n<p>Once the plugin is installed, add it to the <code>gatsby-config.js<\/code> file. From there, add a webpack rule inside the plugin configuration to only load SVG files ending with a certain filename, making it easy to split inline SVGs from other assets:<\/p>\n<pre><code class=\"language-javascript\">\/\/ gatsby-config.js\n\nmodule.exports = {\n plugins: [\n {\n resolve: \"gatsby-plugin-react-svg\",\n\n options: {\n rule: {\n include: \/.inline.svg$\/,\n },\n },\n },\n ],\n};\n<\/code><\/pre>\n<p>Now we can import SVG files like any other React component:<\/p>\n<pre><code class=\"language-javascript\">import * as React from \"react\";\n\nimport Book from \".\/assets\/svg\/book.inline.svg\";\n\nconst GatsbyPluginReactSVG = () => {\n return <Book \/>;\n};\n<\/code><\/pre>\n<p>And just like that, we can use SVGs in our Gatsby pages in several different ways!<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Even though I personally love Gatsby, working with media files has given me more than a few headaches.<\/p>\n<p>As a final tip, when needing common features such as images or querying from your local filesystem, go ahead and install the necessary plugins. But when you need a minor feature, try doing it yourself with the methods that are already available to you!<\/p>\n<p>If you have experienced different headaches when working with media in Gatsby or have circumvented them with different approaches than what I\u2019ve covered, please share them! This is a big space, and it\u2019s always helpful to see how others approach similar challenges.<\/p>\n<p>Again, this article is the first of a brief two-part series on curing headaches when working with media files in a Gatsby project. The <a href=\"https:\/\/www.smashingmagazine.com\/2023\/10\/gatsby-headaches-working-media-part2\/\">following article<\/a> will be about avoiding headaches when working with different media files, including Markdown, PDFs, and 3D models.<\/p>\n<h3 id=\"further-reading\">Further Reading<\/h3>\n<ul>\n<li>\u201c<a href=\"https:\/\/www.smashingmagazine.com\/2022\/06\/demystifying-gatsby4-framework\/\">Demystifying The New Gatsby Framework<\/a>\u201d<\/li>\n<li>\u201c<a href=\"https:\/\/www.smashingmagazine.com\/2023\/06\/gatsby-headaches-i18n-part-1\/\">Gatsby Headaches And How To Cure Them: i18n (Part 1)<\/a>\u201d<\/li>\n<li>\u201c<a href=\"https:\/\/www.smashingmagazine.com\/2023\/06\/gatsby-headaches-i18n-part-2\/\">Gatsby Headaches And How To Cure Them: i18n (Part 2)<\/a>\u201d<\/li>\n<\/ul>\n<div class=\"signature\">\n <img decoding=\"async\" src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" \/><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>Gatsby Headaches: Working With Media (Part 1) Gatsby Headaches: Working With Media (Part 1) Juan Diego Rodr\u00edguez 2023-10-09T11:00:00+00:00 2024-06-12T20:05:40+00:00 Working with media files in Gatsby might not be as straightforward<\/p>\n<p class=\"more-link\"><a href=\"http:\/\/sobre-portugal.com\/index.php\/2023\/10\/09\/gatsby-headaches-working-with-media-part-1\/\" class=\"readmore\">Continue reading<svg class=\"icon icon-arrow-right\" aria-hidden=\"true\" role=\"img\"> <use href=\"#icon-arrow-right\" xlink:href=\"#icon-arrow-right\"><\/use> <\/svg><span class=\"screen-reader-text\">Gatsby Headaches: Working With Media (Part 1)<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"class_list":["post-454","post","type-post","status-publish","format-standard","hentry","category-performance"],"_links":{"self":[{"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/posts\/454","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/comments?post=454"}],"version-history":[{"count":1,"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/posts\/454\/revisions"}],"predecessor-version":[{"id":455,"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/posts\/454\/revisions\/455"}],"wp:attachment":[{"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/media?parent=454"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/categories?post=454"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/sobre-portugal.com\/index.php\/wp-json\/wp\/v2\/tags?post=454"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}