[{"data":1,"prerenderedAt":693},["ShallowReactive",2],{"cms-blog-moving-gatsby-image-processing-to-netlify-image-cdn":3,"cms-blog":20},{"title":4,"excerpt":5,"slug":8,"rawDate":9,"date":10,"modified":11,"content":12,"readingTime":13,"acf":14,"tags":17,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Moving Gatsby Image Processing to Netlify Image CDN",{"raw":6,"plain":7},"\u003Cp>With Netlify&#8217;s acquisition of Gatsby and subsequent shutdown of Gatsby Cloud, it left many users racing to migrate their deployment pipeline to Netlify. Netlify did do well to implement some of Gatsby Cloud&#8217;s features, but the overall experience feels half-baked. Many of the docs are incomplete, or haven&#8217;t been updated in a couple of years. [&hellip;]\u003C\u002Fp>","With Netlify’s acquisition of Gatsby and subsequent shutdown of Gatsby Cloud, it left many users racing to migrate their deployment pipeline to Netlify. Netlify did do well to implement some of Gatsby Cloud’s features, but the overall experience feels half-baked. Many of the docs are incomplete, or haven’t been updated in a couple of years. […]","moving-gatsby-image-processing-to-netlify-image-cdn","2023-12-19T17:36:00",["Date","2023-12-19T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],"\u003Cp class=\"wp-block-paragraph\">With Netlify’s acquisition of Gatsby and subsequent shutdown of Gatsby Cloud, it left many users racing to migrate their deployment pipeline to Netlify.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Netlify did do well to implement some of Gatsby Cloud’s features, but the overall experience feels half-baked. Many of the docs are incomplete, or haven’t been updated in a couple of years.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The site I was working on took over 30 minutes at cold boot to build on Gatsby Cloud. On Netlify, it ran out of memory. Images were taking up the most amount of time, to download from WordPress, then transform them.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">I saw that Gatsby Cloud had an Image CDN service where images would be generated on the fly, rather than at build time. Bingo, that would sort out my build issues. Here’s how I actually got it to work.\u003C\u002Fp>\n\n\n\n\u003Ch2 class=\"wp-block-heading\">The first attempt: Gatsby Image CDN\u003C\u002Fh2>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The original site was using \u003Ccode>gatsby-source-wordpress\u003C\u002Fcode>, \u003Ccode>gatsby-plugin-image\u003C\u002Fcode> and Sharp to download and process images during the build.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">A typical GraphQL query looked something like this:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-graphql torchlight\" data-language=\"graphql\">\u003Ccode data-torchlight-id=\"65a6a927-5cf8-a526-e6a7-e7f59fa1bb98\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 1\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">featuredImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 2\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">node\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 3\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">localFile\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 4\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">childImageSharp\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 5\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">gatsbyImageData\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 6\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">          \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">: \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">570\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 7\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">          \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">: \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">400\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 8\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">          \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">: \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">75\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 9\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        )\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">10\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">11\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">12\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">13\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">featuredImage {\n  node {\n    localFile {\n      childImageSharp {\n        gatsbyImageData(\n          width: 570\n          height: 400\n          quality: 75\n        )\n      }\n    }\n  }\n}\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">That works perfectly well for smaller websites. But when you’ve got thousands of product images coming from WordPress, it’s a lot of work to repeat during a cold build.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The first thing I tried was enabling the Gatsby Image CDN environment variable:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-toml torchlight\" data-language=\"toml\">\u003Ccode data-torchlight-id=\"c2fb3ce7-4e0d-6be9-6302-95560a235294\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">[build.environment]\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">2\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">GATSBY_CLOUD_IMAGE_CDN\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"true\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">[build.environment]\n  GATSBY_CLOUD_IMAGE_CDN = \"true\"\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">I updated Gatsby and \u003Ccode>gatsby-source-wordpress\u003C\u002Fcode> to versions that supported the new image fields, and changed the GraphQL queries to use \u003Ccode>gatsbyImage\u003C\u002Fcode> directly.\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-graphql torchlight\" data-language=\"graphql\">\u003Ccode data-torchlight-id=\"c4ccacca-8643-f3af-944c-d30072330a62\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">featuredImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">2\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">node\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">3\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">gatsbyImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">4\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">: \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">570\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">5\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">: \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">400\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">6\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">: \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">75\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">7\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    )\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">8\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">9\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">featuredImage {\n  node {\n    gatsbyImage(\n      width: 570\n      height: 400\n      quality: 75\n    )\n  }\n}\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The idea was to stop using \u003Ccode>localFile\u003C\u002Fcode> and \u003Ccode>childImageSharp\u003C\u002Fcode> entirely. Rather than downloading images and transforming them during the build, Gatsby could generate the image URLs and leave the actual processing until someone requested them.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Unfortunately, this wasn’t quite the end of it.\u003C\u002Fp>\n\n\n\n\u003Ch2 class=\"wp-block-heading\">Making the images work on Netlify\u003C\u002Fh2>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The problem was that Gatsby was generating image URLs intended for Gatsby’s image service, rather than URLs that Netlify could necessarily serve.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">I spent a while experimenting with Gatsby versions, the Netlify Gatsby plugin, redirects and the image processing integration.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">As a temporary workaround, I ended up writing my own image-processing function using Sharp.\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-js torchlight\" data-language=\"js\">\u003Ccode data-torchlight-id=\"6ba3fc3a-8bfa-d49e-f365-1b2f0e19d5a7\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 1\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">sharp\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">require\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"sharp\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 2\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">fetch\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">require\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"node-fetch\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 3\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 4\u003C\u002Fspan>\u003Cspan style=\"color: #4EC9B0;\">exports\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">handler\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">async\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> (\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">event\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">=&gt;\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 5\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">path\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">event\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">path\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">replace\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #D16969;\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">^\u003C\u002Fspan>\u003Cspan style=\"color: #D7BA7D;\">\\\u002F\u003C\u002Fspan>\u003Cspan style=\"color: #D16969;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #D7BA7D;\">+\\\u002F\u003C\u002Fspan>\u003Cspan style=\"color: #D16969;\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">, \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">''\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 6\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">hash\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">event\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">queryStringParameters\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">hash\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> || \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">''\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 7\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 8\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F Fetch the original image\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 9\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">response\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">await\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">fetch\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">10\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">`\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">${\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">process\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">env\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">DEPLOY_URL\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\u002Fstatic\u002F\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">${\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">hash\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">${\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">path\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">`\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">11\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  )\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">12\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">buffer\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">await\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">response\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">buffer\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">()\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">13\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">14\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F Get the original image dimensions\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">15\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">metadata\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">await\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">sharp\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">buffer\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">).\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">metadata\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">()\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">16\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">17\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">let\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">parseInt\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">event\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">queryStringParameters\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">w\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) || \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">metadata\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">18\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">let\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">parseInt\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">event\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">queryStringParameters\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">h\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) || \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">metadata\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">19\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">format\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">event\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">queryStringParameters\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">fm\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> || \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">'jpeg'\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">20\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">parseInt\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">event\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">queryStringParameters\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">q\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) || \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">60\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">21\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">22\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F Transform the image\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">23\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">processedImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">sharp\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">buffer\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">24\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    .\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">resize\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">, \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">25\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    .\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">toFormat\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">format\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">, { \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> })\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">26\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">27\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F Give up after 3 seconds\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">28\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">timeout\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">new\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4EC9B0;\">Promise\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">((\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">resolve\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">=&gt;\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">29\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">setTimeout\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(() \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">=&gt;\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">resolve\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">buffer\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">), \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">3000\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">30\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  })\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">31\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">32\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">result\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">await\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4EC9B0;\">Promise\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">race\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">([\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">33\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">processedImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">toBuffer\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(),\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">34\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">timeout\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">35\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  ])\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">36\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">37\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">return\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">38\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">statusCode:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">200\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">39\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">headers:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">40\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">'Content-Type'\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">`image\u002F\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">${\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">format\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">`\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">41\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">'Netlify-CDN-Cache-Control'\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">:\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">42\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">'public, max-age=31536000, must-revalidate'\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">43\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    },\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">44\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">body:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">result\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">toString\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">'base64'\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">),\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">45\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">isBase64Encoded:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">true\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">46\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">47\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">48\u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F This is a shortened historical excerpt for the article, not a drop-in replacement.\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">49\u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F The original includes error handling, dimension limits and additional headers.\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">50\u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F Its timeout fallback also returned the original image buffer,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">51\u003C\u002Fspan>\u003Cspan style=\"color: #6A9955;\">\u002F\u002F which could differ from the declared response format.\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">const sharp = require(\"sharp\")\nconst fetch = require(\"node-fetch\")\n\nexports.handler = async (event) =&gt; {\n  const path = event.path.replace(\u002F^\\\u002F.+\\\u002F\u002F, '')\n  const hash = event.queryStringParameters.hash || ''\n\n  \u002F\u002F Fetch the original image\n  const response = await fetch(\n    `${process.env.DEPLOY_URL}\u002Fstatic\u002F${hash}\u002F${path}`\n  )\n  const buffer = await response.buffer()\n\n  \u002F\u002F Get the original image dimensions\n  const metadata = await sharp(buffer).metadata()\n\n  let width = parseInt(event.queryStringParameters.w) || metadata.width\n  let height = parseInt(event.queryStringParameters.h) || metadata.height\n  const format = event.queryStringParameters.fm || 'jpeg'\n  const quality = parseInt(event.queryStringParameters.q) || 60\n\n  \u002F\u002F Transform the image\n  const processedImage = sharp(buffer)\n    .resize(width, height)\n    .toFormat(format, { quality })\n\n  \u002F\u002F Give up after 3 seconds\n  const timeout = new Promise((resolve) =&gt; {\n    setTimeout(() =&gt; resolve(buffer), 3000)\n  })\n\n  const result = await Promise.race([\n    processedImage.toBuffer(),\n    timeout\n  ])\n\n  return {\n    statusCode: 200,\n    headers: {\n      'Content-Type': `image\u002F${format}`,\n      'Netlify-CDN-Cache-Control':\n        'public, max-age=31536000, must-revalidate',\n    },\n    body: result.toString('base64'),\n    isBase64Encoded: true\n  }\n}\n\u002F\u002F This is a shortened historical excerpt for the article, not a drop-in replacement.\n\u002F\u002F The original includes error handling, dimension limits and additional headers.\n\u002F\u002F Its timeout fallback also returned the original image buffer,\n\u002F\u002F which could differ from the declared response format.\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The function downloaded the original image from the Netlify deployment, extracted its dimensions, resized it according to the query parameters, and returned the processed image with CDN caching headers.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">I even added a three-second timeout so an image transformation couldn’t run indefinitely.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">It worked, but it felt ridiculous. I’d migrated away from Gatsby Cloud’s image processing, only to find myself recreating an image CDN using a serverless function.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Thankfully, Netlify eventually introduced its native Image CDN, which meant I could delete the function and let Netlify handle the transformations instead.\u003C\u002Fp>\n\n\n\n\u003Ch2 class=\"wp-block-heading\">Using Netlify Image CDN directly\u003C\u002Fh2>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Instead of relying on Gatsby to generate and transform images, I switched to passing the original WordPress image URL directly to Netlify.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Netlify Image CDN exposes an endpoint at:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"torchlight\">\u003Ccode data-torchlight-id=\"149134ea-f975-2d0f-4c3b-1aab65c70602\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">\u002F.netlify\u002Fimages\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">\u002F.netlify\u002Fimages\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">You supply the image URL and the transformations you want as query parameters.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">For example:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"torchlight\">\u003Ccode data-torchlight-id=\"8cfe6bcd-3636-88d5-be92-a400c10084e0\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">\u002F.netlify\u002Fimages?url=https%3A%2F%2Fexample.com%2Fimage.jpg&amp;w=570&amp;q=75\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">\u002F.netlify\u002Fimages?url=https%3A%2F%2Fexample.com%2Fimage.jpg&amp;w=570&amp;q=75\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">This tells Netlify to fetch the original image, resize it to 570 pixels wide and serve an optimised version.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The important difference is that the image isn’t downloaded and processed as part of the Gatsby build.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Netlify handles the transformation when the image is requested and caches the result for subsequent visitors.\u003C\u002Fp>\n\n\n\n\u003Ch3 class=\"wp-block-heading\">Allow remote images\u003C\u002Fh3>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">By default, Netlify won’t fetch images from arbitrary external domains. You need to explicitly allow the source domains in \u003Ccode>netlify.toml\u003C\u002Fcode>.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">For a WordPress website, add:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-toml torchlight\" data-language=\"toml\">\u003Ccode data-torchlight-id=\"dae69f02-3bca-1619-07c4-abd137252270\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">[images]\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">2\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">remote_images\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = [\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">3\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"https:\u002F\u002Fwordpress.example.com\u002F.*\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">4\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  ]\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">[images]\n  remote_images = [\n    \"https:\u002F\u002Fwordpress.example.com\u002F.*\"\n  ]\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Replace the example domain with the domain hosting your WordPress media.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">If you’ve got separate staging and production WordPress environments, you’ll need to allow both.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">For this project, WordPress was hosted on Kinsta, and we eventually added the staging domains too after discovering that images weren’t working properly in previews.\u003C\u002Fp>\n\n\n\n\u003Ch3 class=\"wp-block-heading\">Create a reusable image component\u003C\u002Fh3>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The next step was replacing the Gatsby image components with a small React component that generated the Netlify image URL.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The basic implementation is straightforward:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-jsx torchlight\" data-language=\"jsx\">\u003Ccode data-torchlight-id=\"ed1ae1bd-7ccc-b892-27de-42191d643b29\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 1\u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">import\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">React\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">from\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"react\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">;\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 2\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 3\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">transformImageUrl\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = ({\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 4\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">imageUrl\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 5\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 6\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 7\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">75\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 8\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">fit\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"contain\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\"> 9\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}) \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">=&gt;\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">10\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">if\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> (!\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">imageUrl\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">return\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">;\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">11\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">12\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">const\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4FC1FF;\">params\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">new\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">URLSearchParams\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">({\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">13\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">url:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">imageUrl\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">14\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">q:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #4EC9B0;\">String\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">),\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">15\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  });\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">16\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">17\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">if\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> (\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">params\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">set\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"w\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">, \u003C\u002Fspan>\u003Cspan style=\"color: #4EC9B0;\">String\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">));\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">18\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">if\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> (\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">params\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">set\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"h\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">, \u003C\u002Fspan>\u003Cspan style=\"color: #4EC9B0;\">String\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">));\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">19\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">20\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">if\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> (\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> &amp;&amp; \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">) {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">21\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">params\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">set\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"fit\"\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">, \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">fit\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">);\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">22\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">23\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">24\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">return\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">`\u002F.netlify\u002Fimages?\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">${\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">params\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">toString\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">()\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">`\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">;\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">25\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">};\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">26\u003C\u002Fspan>&nbsp;\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">27\u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">export\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">default\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">function\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">Image\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">({\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">28\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">image\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">29\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">alt\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">30\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">31\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">32\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> = \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">75\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">33\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}) {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">34\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #C586C0;\">return\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> (\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">35\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #808080;\">&lt;\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">img\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">36\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">src\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">transformImageUrl\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">({\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">37\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">imageUrl:\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">image\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">38\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">39\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">40\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">quality\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">,\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">41\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      })\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">42\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">alt\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">alt\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">43\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">44\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">45\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">loading\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"lazy\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">46\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">decoding\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #CE9178;\">\"async\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">47\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #808080;\">\u002F&gt;\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">48\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  );\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">49\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">import React from \"react\";\n\nconst transformImageUrl = ({\n  imageUrl,\n  width,\n  height,\n  quality = 75,\n  fit = \"contain\",\n}) =&gt; {\n  if (!imageUrl) return \"\";\n\n  const params = new URLSearchParams({\n    url: imageUrl,\n    q: String(quality),\n  });\n\n  if (width) params.set(\"w\", String(width));\n  if (height) params.set(\"h\", String(height));\n\n  if (width &amp;&amp; height) {\n    params.set(\"fit\", fit);\n  }\n\n  return `\u002F.netlify\u002Fimages?${params.toString()}`;\n};\n\nexport default function Image({\n  image,\n  alt,\n  width,\n  height,\n  quality = 75,\n}) {\n  return (\n    &lt;img\n      src={transformImageUrl({\n        imageUrl: image,\n        width,\n        height,\n        quality,\n      })}\n      alt={alt}\n      width={width}\n      height={height}\n      loading=\"lazy\"\n      decoding=\"async\"\n    \u002F&gt;\n  );\n}\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Now I could use an image directly from its WordPress URL:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-jsx torchlight\" data-language=\"jsx\">\u003Ccode data-torchlight-id=\"be2abdb8-fb7c-3eda-4f6b-025baff3512d\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #808080;\">&lt;\u003C\u002Fspan>\u003Cspan style=\"color: #4EC9B0;\">Image\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">2\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">image\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">product\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">featuredImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">node\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">mediaItemUrl\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">3\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">alt\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">product\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">.\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">title\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">4\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">570\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">5\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">height\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">=\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">{\u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">400\u003C\u002Fspan>\u003Cspan style=\"color: #569CD6;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">6\u003C\u002Fspan>\u003Cspan style=\"color: #808080;\">\u002F&gt;\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">&lt;Image\n  image={product.featuredImage.node.mediaItemUrl}\n  alt={product.title}\n  width={570}\n  height={400}\n\u002F&gt;\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">No \u003Ccode>getImage()\u003C\u002Fcode>, no \u003Ccode>GatsbyImage\u003C\u002Fcode>, and no Sharp transformation during the build.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">I kept this as a reusable component because there were images throughout the site: product cards, galleries, hero sections, footer logos and content blocks.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">A single component made it much easier to keep the behaviour consistent.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The example above uses a relative URL. Our actual implementation also supports an absolute site URL, which is useful when generating images for SEO metadata or external feeds.\u003C\u002Fp>\n\n\n\n\u003Ch2 class=\"wp-block-heading\">Update your GraphQL queries\u003C\u002Fh2>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The final part was making sure Gatsby wasn’t still downloading images unnecessarily.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">We had a lot of queries using \u003Ccode>localFile\u003C\u002Fcode>, including cases where the image was only needed to display in a normal \u003Ccode>&lt;img&gt;\u003C\u002Fcode> element.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">For those, I switched to the original WordPress media URL.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Instead of:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-graphql torchlight\" data-language=\"graphql\">\u003Ccode data-torchlight-id=\"51f6ddca-c1bc-34b7-8246-a9d81b867f65\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">featuredImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">2\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">node\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">3\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">localFile\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">4\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">childImageSharp\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">5\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">        \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">gatsbyImageData\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">(\u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">width\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">: \u003C\u002Fspan>\u003Cspan style=\"color: #B5CEA8;\">570\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">)\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">6\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">      }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">7\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">8\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">9\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">featuredImage {\n  node {\n    localFile {\n      childImageSharp {\n        gatsbyImageData(width: 570)\n      }\n    }\n  }\n}\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">We could use:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"language-graphql torchlight\" data-language=\"graphql\">\u003Ccode data-torchlight-id=\"c24df080-7774-1e8a-30a4-b933a00932cf\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #DCDCAA;\">featuredImage\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">2\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">node\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\"> {\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">3\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">mediaItemUrl\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">4\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">    \u003C\u002Fspan>\u003Cspan style=\"color: #9CDCFE;\">altText\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">5\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">  }\u003C\u002Fspan>\u003C\u002Fdiv>\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">6\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">featuredImage {\n  node {\n    mediaItemUrl\n    altText\n  }\n}\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">This is an important part of the migration. Just changing the React component isn’t enough if Gatsby is still being instructed to download and process images elsewhere in the site.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">You also need to check other uses of image data. We had images in the product gallery, content components, hero backgrounds and SEO metadata.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Some of these needed slightly different treatment. CSS background images still needed transformed URLs, while social sharing images needed absolute URLs rather than relative paths.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">SVG files were another exception. These could simply be passed through without image transformation.\u003C\u002Fp>\n\n\n\n\u003Ch2 class=\"wp-block-heading\">A few things that caught me out\u003C\u002Fh2>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">The final solution was much simpler than the original implementation, but it still took a few rounds of fixes to get everything working correctly.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">\u003Cstrong>Staging and preview domains.\u003C\u002Fstrong> Netlify needs to be allowed to fetch images from whichever WordPress environment supplied them. Forgetting a staging hostname can leave images working in production but broken in preview deployments.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">\u003Cstrong>Images in SEO metadata.\u003C\u002Fstrong> Open Graph images and structured data need URLs that external services can actually access. Relative image paths aren’t suitable for those uses.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">\u003Cstrong>Image dimensions.\u003C\u002Fstrong> Netlify’s transformation parameters expect valid numeric dimensions. We later had to round width and height values to prevent errors when calculated sizes contained decimals.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">\u003Cstrong>Responsive images.\u003C\u002Fstrong> Replacing \u003Ccode>GatsbyImage\u003C\u002Fcode> with a plain \u003Ccode>&lt;img&gt;\u003C\u002Fcode> means losing Gatsby’s automatically generated responsive \u003Ccode>srcSet\u003C\u002Fcode> and \u003Ccode>sizes\u003C\u002Fcode>. We subsequently added our own responsive variants, including different widths and device pixel ratios.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">\u003Cstrong>Image formats.\u003C\u002Fstrong> Netlify can negotiate efficient formats such as WebP and AVIF automatically, but there are also options to request a specific format. We later extended the component to support this.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">And finally, make sure you’ve removed the old image queries that are no longer needed. Otherwise, it’s possible to have all your images displaying through Netlify while Gatsby continues doing unnecessary work behind the scenes.\u003C\u002Fp>\n\n\n\n\u003Ch2 class=\"wp-block-heading\">What about the official Gatsby integration?\u003C\u002Fh2>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Since I originally implemented this, Netlify’s Gatsby integration has improved.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">For Gatsby 5.13 and later, the supported approach is to enable:\u003C\u002Fp>\n\n\n\n\u003Cpre class=\"torchlight\">\u003Ccode data-torchlight-id=\"98fbfca7-e100-d571-6190-73a0d0cd5abc\">\u003C!-- Syntax highlighted by torchlight.dev -->\u003Cdiv class=\"line\">\u003Cspan style=\"color:#858585; text-align: right; -webkit-user-select: none; user-select: none;\" class=\"line-number\">1\u003C\u002Fspan>\u003Cspan style=\"color: #D4D4D4;\">NETLIFY_IMAGE_CDN=true\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fcode>\n    \u003Cdiv class=\"copy-button__container js-copy-to-clipboard-container\">\n      \u003Cdiv class=\"copy-button__text js-copy-to-clipboard-notification\">Copied!\u003C\u002Fdiv>\n      \u003Cbutton type=\"button\" title=\"Copy to clipboard\" class=\"copy-button__button js-copy-to-clipboard-button\">\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cspan class=\"torchlight-copy-target js-copy-to-clipboard-target\" style=\"display: none\">NETLIFY_IMAGE_CDN=true\u003C\u002Fspan>\n    \u003C\u002Fpre>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Along with a supported source plugin and the appropriate remote image configuration.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">This allows Gatsby’s image components to use deferred image processing without manually rebuilding the image handling.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">It’s worth trying that first if you’re starting a new migration.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">However, the direct \u003Ccode>\u002F.netlify\u002Fimages\u003C\u002Fcode> approach remains useful when you want more control over the image URLs, or when you’ve got a mixture of Gatsby images, CSS backgrounds and media URLs coming directly from a CMS.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">You don’t have to abandon the rest of Gatsby to use it.\u003C\u002Fp>\n\n\n\n\u003Ch2 class=\"wp-block-heading\">Was it worth it?\u003C\u002Fh2>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Absolutely.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Before the migration, a cold build took over 30 minutes on Gatsby Cloud. On Netlify, the same build would run out of memory before completing.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">After moving image processing to Netlify Image CDN, build times dropped to around \u003Cstrong>10 minutes cold and 2 minutes warm\u003C\u002Fstrong>.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">That’s at least a 67% reduction in cold build time, with subsequent builds completing considerably faster thanks to caching.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">It wasn’t a one-line configuration change, despite how simple the idea sounded. There were several attempts at getting Gatsby’s own Image CDN integration working before I ended up using Netlify’s native service directly.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">But the final solution was relatively straightforward: allow the WordPress image domains, generate Netlify Image CDN URLs, and stop asking Gatsby to download and transform images it didn’t need.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">Sometimes the easiest way to fix a slow build is to stop making the build do the work in the first place.\u003C\u002Fp>\n\n\n\n\u003Cp class=\"wp-block-paragraph\">\u003C\u002Fp>",7,{"project_url":15,"github":15,"icons":16},"",[],[],"Joe",null,[21,131,250,374,481,586],[22,34,46,58,69,80,87,98,108,119],{"title":23,"excerpt":24,"slug":27,"date":28,"modified":29,"readingTime":30,"acf":31,"tags":33,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"How AI helped me add my Mitsubishi Delica D:5 to UK insurance databases",{"raw":25,"plain":26},"\u003Cp>Earlier this year I bought a 2007 Mitsubishi Delica D:5. The D:5 has been sold in Japan since 2007 but was never officially sold in the UK. Mine is a Japanese import, which means that while it is perfectly legal and registered with the DVLA, getting insurance for it can be a little more complicated [&hellip;]\u003C\u002Fp>","Earlier this year I bought a 2007 Mitsubishi Delica D:5. The D:5 has been sold in Japan since 2007 but was never officially sold in the UK. Mine is a Japanese import, which means that while it is perfectly legal and registered with the DVLA, getting insurance for it can be a little more complicated […]","how-ai-helped-me-add-my-mitsubishi-delica-d5-to-uk-insurance-databases",["Date","2026-11-06T00:00:00.000Z"],["Date","2026-11-06T00:00:00.000Z"],8,{"project_url":15,"github":15,"icons":32},[],[],{"title":35,"excerpt":36,"slug":39,"date":40,"modified":41,"readingTime":42,"acf":43,"tags":45,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Moving my site from Netlify to Cloudflare Workers",{"raw":37,"plain":38},"\u003Cp>I moved this site&#8217;s builds, hosting, forms and images from Netlify to Cloudflare Workers, and upgraded it to Nuxt 4 and TypeScript along the way. Here&#8217;s what moved where, and what caught me out.\u003C\u002Fp>","I moved this site’s builds, hosting, forms and images from Netlify to Cloudflare Workers, and upgraded it to Nuxt 4 and TypeScript along the way. Here’s what moved where, and what caught me out.","moving-from-netlify-to-cloudflare-workers",["Date","2026-10-27T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],2,{"project_url":15,"github":15,"icons":44},[],[],{"title":47,"excerpt":48,"slug":51,"date":52,"modified":53,"readingTime":54,"acf":55,"tags":57,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Scaling ECS on PHP-FPM workers instead of CPU and memory",{"raw":49,"plain":50},"\u003Cp>Our ECS services scaled on CPU and memory, which meant traffic spikes took the platform down before a new container ever started. Scaling on busy PHP-FPM workers fixed the outages and let us run 3 containers instead of 8.\u003C\u002Fp>","Our ECS services scaled on CPU and memory, which meant traffic spikes took the platform down before a new container ever started. Scaling on busy PHP-FPM workers fixed the outages and let us run 3 containers instead of 8.","scaling-ecs-on-php-fpm-workers",["Date","2026-09-15T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],9,{"project_url":15,"github":15,"icons":56},[],[],{"title":59,"excerpt":60,"slug":63,"date":64,"modified":65,"readingTime":66,"acf":67,"tags":68,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"How to download a Google Sheet on a cron",{"raw":61,"plain":62},"\u003Cp>Working with Google Cloud can be confusing for beginners and sometimes all you need is to manipulate a Google Sheet. I have a couple of use cases for downloading a sheet and uploading a sheet. I haven&#8217;t quite figured out the upload part yet, but I have got downloading to work. I&#8217;ve decided to write [&hellip;]\u003C\u002Fp>","Working with Google Cloud can be confusing for beginners and sometimes all you need is to manipulate a Google Sheet. I have a couple of use cases for downloading a sheet and uploading a sheet. I haven’t quite figured out the upload part yet, but I have got downloading to work. I’ve decided to write […]","how-to-download-a-google-sheet-on-a-cron",["Date","2024-02-22T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],4,{"project_url":15,"github":15,"icons":15},[],{"title":70,"excerpt":71,"slug":74,"date":75,"modified":76,"readingTime":77,"acf":78,"tags":79,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"How to add Custom Variables to LiveChat",{"raw":72,"plain":73},"\u003Cp>I had a task to add a Google Analytics ID to the user profile in LiveChat. We wanted to be able to match up the person we were chatting to with the analytics data in Google. This looked really simple according to their docs. You can just use the following function: What their docs failed [&hellip;]\u003C\u002Fp>","I had a task to add a Google Analytics ID to the user profile in LiveChat. We wanted to be able to match up the person we were chatting to with the analytics data in Google. This looked really simple according to their docs. You can just use the following function: What their docs failed […]","how-to-add-custom-variables-to-livechat",["Date","2024-01-16T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],1,{"project_url":15,"github":15,"icons":15},[],{"title":4,"excerpt":81,"slug":8,"date":82,"modified":83,"readingTime":13,"acf":84,"tags":86,"author":18,"featuredMediaUrl":19,"featuredMedia":19},{"raw":6,"plain":7},["Date","2023-12-19T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],{"project_url":15,"github":15,"icons":85},[],[],{"title":88,"excerpt":89,"slug":92,"date":93,"modified":94,"readingTime":66,"acf":95,"tags":97,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Creating a reusable date component for Nuxt",{"raw":90,"plain":91},"\u003Cp>When I was building my personal website with Nuxt, I wanted something a bit more interesting than a plain text date next to each blog post. I ended up creating a small date component which displays the day in a coloured block, with the month and year underneath in a ribbon shape. It&#8217;s a fairly [&hellip;]\u003C\u002Fp>","When I was building my personal website with Nuxt, I wanted something a bit more interesting than a plain text date next to each blog post. I ended up creating a small date component which displays the day in a coloured block, with the month and year underneath in a ribbon shape. It’s a fairly […]","creating-a-reusable-date-component-for-nuxt",["Date","2023-11-20T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],{"project_url":15,"github":15,"icons":96},[],[],{"title":99,"excerpt":100,"slug":103,"date":104,"modified":105,"readingTime":42,"acf":106,"tags":107,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"This one Nuxt trick prevents broken deploys",{"raw":101,"plain":102},"\u003Cp>Have you ever experienced broken deploys while using Nuxt? It can be frustrating when your build encounters errors, but still proceeds to deploy all the files, resulting in broken pages on your website. Fortunately, I discovered a handy Nuxt trick that prevents such broken deploys and ensures a more reliable deployment process. In my case, [&hellip;]\u003C\u002Fp>","Have you ever experienced broken deploys while using Nuxt? It can be frustrating when your build encounters errors, but still proceeds to deploy all the files, resulting in broken pages on your website. Fortunately, I discovered a handy Nuxt trick that prevents such broken deploys and ensures a more reliable deployment process. In my case, […]","this-one-nuxt-trick-prevents-broken-deploys",["Date","2023-07-12T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":109,"excerpt":110,"slug":113,"date":114,"modified":115,"readingTime":116,"acf":117,"tags":118,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"The importance of read time for blog posts and how to automatically generate it using Node.js",{"raw":111,"plain":112},"\u003Cp>In today&#8217;s fast-paced digital world, attention spans are getting shorter, and users often have limited time to consume content. As a result, providing readers with an estimated reading time can be a valuable feature for any blog or content-based website. Why having a read time for a blog post is important Automatically generate a read [&hellip;]\u003C\u002Fp>","In today’s fast-paced digital world, attention spans are getting shorter, and users often have limited time to consume content. As a result, providing readers with an estimated reading time can be a valuable feature for any blog or content-based website. Why having a read time for a blog post is important Automatically generate a read […]","the-importance-of-read-time-for-blog-posts-and-how-to-automatically-generate-it-using-node-js",["Date","2023-06-20T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],3,{"project_url":15,"github":15,"icons":15},[],{"title":120,"excerpt":121,"slug":124,"date":125,"modified":126,"readingTime":30,"acf":127,"tags":128,"author":18,"featuredMediaUrl":129,"featuredMedia":130},"Adding syntax highlighting to my blog with Torchlight",{"raw":122,"plain":123},"\u003Cp>Background A little while ago I read the following blog post, Web Component for a Code Block by Chris Coyier, it got me thinking about how I could enhance the code blocks on my website. This is a tech blog after all, and from time to time I want to share tips and tricks which involve [&hellip;]\u003C\u002Fp>","Background A little while ago I read the following blog post, Web Component for a Code Block by Chris Coyier, it got me thinking about how I could enhance the code blocks on my website. This is a tech blog after all, and from time to time I want to share tips and tricks which involve […]","adding-syntax-highlighting-to-my-blog-with-torchlight",["Date","2023-06-16T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],"https:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002Fe1e861af4f6259527d0fc8ad9790f0070bbbbc9e3026b722390c4894a9472d28\u002Fe1e861af4f625952-NVIDIA-GeForce-Overlay-DT-2023-06-16-14.07.00-1.png","https:\u002F\u002Fjoebailey.xyz\u002Fcdn-cgi\u002Fimage\u002Fformat=auto,quality=80,fit=scale-down,width=1200\u002Fhttps:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002Fe1e861af4f6259527d0fc8ad9790f0070bbbbc9e3026b722390c4894a9472d28\u002Fe1e861af4f625952-NVIDIA-GeForce-Overlay-DT-2023-06-16-14.07.00-1.png",[132,144,157,167,179,190,200,214,227,240],{"title":133,"excerpt":134,"slug":137,"date":138,"modified":139,"readingTime":66,"acf":140,"tags":141,"author":18,"featuredMediaUrl":142,"featuredMedia":143},"Adding Algolia search to my 404 page",{"raw":135,"plain":136},"\u003Cp>My site is pretty small, so I don&#8217;t really need search functionality, but the Netlify Algolia plugin looked easy enough to use and a bit of fun. Diving into the docs, I got a bit overwhelmed. Algolia&#8217;s breadth of capabilities is huge. They definitely seem like the go-to for fully customisable search on a Jamstack [&hellip;]\u003C\u002Fp>","My site is pretty small, so I don’t really need search functionality, but the Netlify Algolia plugin looked easy enough to use and a bit of fun. Diving into the docs, I got a bit overwhelmed. Algolia’s breadth of capabilities is huge. They definitely seem like the go-to for fully customisable search on a Jamstack […]","adding-algolia-search-to-my-404-page",["Date","2023-06-13T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],"https:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F109426a35d77479e1e989a82f03158349304fd98349223e9939110f26ef80377\u002F109426a35d77479e-NVIDIA-GeForce-Overlay-DT-2023-06-13-21.09.10.png","https:\u002F\u002Fjoebailey.xyz\u002Fcdn-cgi\u002Fimage\u002Fformat=auto,quality=80,fit=scale-down,width=1200\u002Fhttps:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F109426a35d77479e1e989a82f03158349304fd98349223e9939110f26ef80377\u002F109426a35d77479e-NVIDIA-GeForce-Overlay-DT-2023-06-13-21.09.10.png",{"title":145,"excerpt":146,"slug":149,"date":150,"modified":151,"readingTime":66,"acf":152,"tags":156,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Notification of a delivery failure in Mailgun",{"raw":147,"plain":148},"\u003Cp>When you use an email service provider like Gmail, you get delivery failure emails. These emails are great because it enables you to reach out to your intended recipient in another way, or just retry the email later on. Because I use Mailgun with Gmail&#8217;s Send Mail As feature for my custom domains, I needed [&hellip;]\u003C\u002Fp>","When you use an email service provider like Gmail, you get delivery failure emails. These emails are great because it enables you to reach out to your intended recipient in another way, or just retry the email later on. Because I use Mailgun with Gmail’s Send Mail As feature for my custom domains, I needed […]","notification-of-a-delivery-failure-in-mailgun",["Date","2023-05-31T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],{"project_url":15,"github":153,"icons":154},"https:\u002F\u002Fgithub.com\u002FGalexia-Agency\u002Fmailgun-failed-webhook",[155],"Custom",[],{"title":158,"excerpt":159,"slug":162,"date":163,"modified":164,"readingTime":77,"acf":165,"tags":166,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Dragging to Scroll with JavaScript",{"raw":160,"plain":161},"\u003Cp>My portfolio has some cards to showcase projects and blog posts. On mobile, these cards display in a horizontal slider, which is easy enough to scroll on a touchscreen, or trackpad, but what if someone is viewing the website at a small size on a device with a mouse? Well they can of course use [&hellip;]\u003C\u002Fp>","My portfolio has some cards to showcase projects and blog posts. On mobile, these cards display in a horizontal slider, which is easy enough to scroll on a touchscreen, or trackpad, but what if someone is viewing the website at a small size on a device with a mouse? Well they can of course use […]","dragging-to-scroll-with-javascript",["Date","2023-03-30T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":168,"excerpt":169,"slug":172,"date":173,"modified":174,"readingTime":175,"acf":176,"tags":178,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Using Google Sheets as a database for a Vue application",{"raw":170,"plain":171},"\u003Cp>I had a client who needed to display a list of churches taking part in an event. The list needed to be organised by area, include information about each church, and allow visitors to download a PDF they could print off. The information was already being managed in Google Sheets, so rather than move everything [&hellip;]\u003C\u002Fp>","I had a client who needed to display a list of churches taking part in an event. The list needed to be organised by area, include information about each church, and allow visitors to download a PDF they could print off. The information was already being managed in Google Sheets, so rather than move everything […]","using-google-sheets-as-a-database-for-a-vue-application",["Date","2022-11-08T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],5,{"project_url":15,"github":15,"icons":177},[],[],{"title":180,"excerpt":181,"slug":184,"date":185,"modified":186,"readingTime":66,"acf":187,"tags":189,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Scraping a website to migrate it to WordPress",{"raw":182,"plain":183},"\u003Cp>I was tasked with migrating an old website from Yahoo Pages to WordPress. Normally, migrating a website is fairly straightforward if you can export its content or access the database. Unfortunately, that wasn&#8217;t an option here. The website contained a collection of articles about local and world history. I needed to get that content out [&hellip;]\u003C\u002Fp>","I was tasked with migrating an old website from Yahoo Pages to WordPress. Normally, migrating a website is fairly straightforward if you can export its content or access the database. Unfortunately, that wasn’t an option here. The website contained a collection of articles about local and world history. I needed to get that content out […]","scraping-a-website-to-migrate-it-to-wordpress",["Date","2021-03-08T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],{"project_url":15,"github":15,"icons":188},[],[],{"title":191,"excerpt":192,"slug":195,"date":196,"modified":197,"readingTime":42,"acf":198,"tags":199,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"How To Secure your server using UFW and Cloudflare",{"raw":193,"plain":194},"\u003Cp>This guide will help you use UFW (uncomplicated firewall) to block all traffic to your server other than traffic coming via Cloudflare, or for essential operations such as FTP and SSH. Why would you want to do this? Cloudflare will protect your site from all manner of attacks. However, your server may still be vulnerable. [&hellip;]\u003C\u002Fp>","This guide will help you use UFW (uncomplicated firewall) to block all traffic to your server other than traffic coming via Cloudflare, or for essential operations such as FTP and SSH. Why would you want to do this? Cloudflare will protect your site from all manner of attacks. However, your server may still be vulnerable. […]","how-to-secure-your-server-using-ufw-and-cloudflare",["Date","2021-01-11T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":201,"excerpt":202,"slug":205,"date":206,"modified":207,"readingTime":77,"acf":208,"tags":213,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Adobe XD Challenge Calculator",{"raw":203,"plain":204},"\u003Cp>The third challenge was to create a calculator. I wanted to practise my CSS art and so chose an image of an old Casio calculator and tried to recreate it as closely as possible. It was quite a challenge creating the code for the calculator to function. It wasn&#8217;t as simple as just adding numbers [&hellip;]\u003C\u002Fp>","The third challenge was to create a calculator. I wanted to practise my CSS art and so chose an image of an old Casio calculator and tried to recreate it as closely as possible. It was quite a challenge creating the code for the calculator to function. It wasn’t as simple as just adding numbers […]","adobe-xd-challenge-calculator",["Date","2020-08-06T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":209,"github":210,"icons":211},"https:\u002F\u002Fvue-calculator-joe.netlify.app","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FVue-Calculator",[212],"Vue",[],{"title":215,"excerpt":216,"slug":219,"date":220,"modified":221,"readingTime":77,"acf":222,"tags":226,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Adobe XD Challenge Fitness App",{"raw":217,"plain":218},"\u003Cp>The second challenge was to create a fitness app. I chose to make an activity tracker. I took inspiration from Lifelog, an app by Sony that I used for many years. I used the Google Maps API and created a component in Vue for the activities.\u003C\u002Fp>","The second challenge was to create a fitness app. I chose to make an activity tracker. I took inspiration from Lifelog, an app by Sony that I used for many years. I used the Google Maps API and created a component in Vue for the activities.","adobe-xd-challenge-fitness-app",["Date","2020-08-06T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":223,"github":224,"icons":225},"https:\u002F\u002Fjoe-exercise.netlify.app","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FExercise-App",[212],[],{"title":228,"excerpt":229,"slug":232,"date":233,"modified":234,"readingTime":77,"acf":235,"tags":239,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Adobe XD Challenge – Music Player",{"raw":230,"plain":231},"\u003Cp>Me and Eleanor Morgan participated in the Adobe XD Challenge. I wanted to use Vue.js rather than practise my skills in XD as I view myself as more of a developer than a designer. The first challenge was to create a music player. I played around with the audio API and used some CSS animations.\u003C\u002Fp>","Me and Eleanor Morgan participated in the Adobe XD Challenge. I wanted to use Vue.js rather than practise my skills in XD as I view myself as more of a developer than a designer. The first challenge was to create a music player. I played around with the audio API and used some CSS animations.","adobe-xd-challenge-music-player",["Date","2020-08-06T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":236,"github":237,"icons":238},"http:\u002F\u002Fmusic-player.joebailey.xyz\u002F","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002Fmusic-player",[212],[],{"title":241,"excerpt":242,"slug":245,"date":246,"modified":247,"readingTime":116,"acf":248,"tags":249,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"React and Vue re-visited",{"raw":243,"plain":244},"\u003Cp>After re-creating my portfolio with Gatsby (which uses React) and building an app for a client in Vue I feel I have more knowledge about the two JavaScript frameworks to give an informed opinion on both. When I was first starting out using these sort of frameworks I wrote this article on my thoughts. I [&hellip;]\u003C\u002Fp>","After re-creating my portfolio with Gatsby (which uses React) and building an app for a client in Vue I feel I have more knowledge about the two JavaScript frameworks to give an informed opinion on both. When I was first starting out using these sort of frameworks I wrote this article on my thoughts. I […]","react-and-vue-re-visited",["Date","2020-05-09T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],[251,261,275,287,300,310,324,338,351,364],{"title":252,"excerpt":253,"slug":256,"date":257,"modified":258,"readingTime":42,"acf":259,"tags":260,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"What is a Front-End Developer?",{"raw":254,"plain":255},"\u003Cp>I&#8217;ve been asking myself this question a lot recently after applying for quite a few web development placements. The experience has been enlightening, teaching me a great deal about the industry. I&#8217;ve been reflecting on what I&#8217;m good at in web development and what I enjoy. Whilst freelancing I&#8217;d describe myself as a Full-Stack Developer. [&hellip;]\u003C\u002Fp>","I’ve been asking myself this question a lot recently after applying for quite a few web development placements. The experience has been enlightening, teaching me a great deal about the industry. I’ve been reflecting on what I’m good at in web development and what I enjoy. Whilst freelancing I’d describe myself as a Full-Stack Developer. […]","what-is-a-front-end-developer",["Date","2020-05-07T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":262,"excerpt":263,"slug":266,"date":267,"modified":268,"readingTime":77,"acf":269,"tags":274,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Points Of Interest",{"raw":264,"plain":265},"\u003Cp>Following on from developing the HitTastic! site we had to create a site in PHP that allows users to view POI&#8217;s stored in a database, and contribute to this database. We had to do some basic styles to go along with it.\u003C\u002Fp>","Following on from developing the HitTastic! site we had to create a site in PHP that allows users to view POI’s stored in a database, and contribute to this database. We had to do some basic styles to go along with it.","points-of-interest",["Date","2020-04-28T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":270,"github":271,"icons":272},"https:\u002F\u002Fpoi.joebailey.xyz\u002F","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FPointsOfInterest-MySQLi",[273],"PHP",[],{"title":276,"excerpt":277,"slug":280,"date":281,"modified":282,"readingTime":77,"acf":283,"tags":286,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"HitTastic!",{"raw":278,"plain":279},"\u003Cp>In my second year of university, I did a PHP unit where we were tasked with creating a site showing the number one songs from a certain year. We had to make a login, connect to a database, and perform a search.\u003C\u002Fp>","In my second year of university, I did a PHP unit where we were tasked with creating a site showing the number one songs from a certain year. We had to make a login, connect to a database, and perform a search.","hittastic",["Date","2020-04-28T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":284,"icons":285},"https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FPHP-Lessons",[273],[],{"title":288,"excerpt":289,"slug":292,"date":293,"modified":294,"readingTime":77,"acf":295,"tags":299,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Hangman",{"raw":290,"plain":291},"\u003Cp>Whilst I was searching for a placement I had a few tasks to complete for various roles I was interviewing for. One of these tasks was to create a simple Hangman game in PHP.\u003C\u002Fp>","Whilst I was searching for a placement I had a few tasks to complete for various roles I was interviewing for. One of these tasks was to create a simple Hangman game in PHP.","hangman",["Date","2020-04-28T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":296,"github":297,"icons":298},"https:\u002F\u002Fhangman.joebailey.xyz\u002F","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FHangman",[273],[],{"title":301,"excerpt":302,"slug":305,"date":306,"modified":307,"readingTime":42,"acf":308,"tags":309,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Re-creating my portfolio with Gatsby",{"raw":303,"plain":304},"\u003Cp>You may have read Re-creating my portfolio with React where I talked about my experiences converting my simple static portfolio into a React website, turning reusable objects on the page into React components. This inspired me to learn Gatsby, a static site generator that uses React. I was previously using my a category on my [&hellip;]\u003C\u002Fp>","You may have read Re-creating my portfolio with React where I talked about my experiences converting my simple static portfolio into a React website, turning reusable objects on the page into React components. This inspired me to learn Gatsby, a static site generator that uses React. I was previously using my a category on my […]","re-creating-my-portfolio-with-gatsby",["Date","2020-04-27T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":311,"excerpt":312,"slug":315,"date":316,"modified":317,"readingTime":77,"acf":318,"tags":323,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Firebase Chat App",{"raw":313,"plain":314},"\u003Cp>In my first year of University I had a go at creating a chat app using Firebase following my lecturers instructions. I was the only person in my class to succeed. Go ahead, type something.\u003C\u002Fp>","In my first year of University I had a go at creating a chat app using Firebase following my lecturers instructions. I was the only person in my class to succeed. Go ahead, type something.","firebase-chat-app",["Date","2020-04-24T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":319,"github":320,"icons":321},"https:\u002F\u002Fchat-app-a7f51.firebaseapp.com\u002F","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002Ffirebase_chat_app",[322],"Firebase",[],{"title":325,"excerpt":326,"slug":329,"date":330,"modified":331,"readingTime":77,"acf":332,"tags":337,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"React Tic Tac Toe",{"raw":327,"plain":328},"\u003Cp>My first venture into the world of React was building this Tic Tac Toe game using the React Docs. It also helped me get to grips with CodePen for creating small little projects.\u003C\u002Fp>","My first venture into the world of React was building this Tic Tac Toe game using the React Docs. It also helped me get to grips with CodePen for creating small little projects.","react-tic-tac-toe",["Date","2020-04-24T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":333,"github":334,"icons":335},"https:\u002F\u002Fcodepen.io\u002Fjoebailey26\u002Fpen\u002FpoogwjN","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FReact-Tic-Tac-Toe",[336],"React",[],{"title":339,"excerpt":340,"slug":343,"date":344,"modified":345,"readingTime":77,"acf":346,"tags":350,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Vue To-Do List",{"raw":341,"plain":342},"\u003Cp>After experimenting using Vue in my Photography Website I decided to have a go at making a To-Do List. You can create and delete To Dos but they don&#8217;t currently persist. This is something I wish to add in the future.\u003C\u002Fp>","After experimenting using Vue in my Photography Website I decided to have a go at making a To-Do List. You can create and delete To Dos but they don’t currently persist. This is something I wish to add in the future.","vue-to-do-list",["Date","2020-04-24T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":347,"github":348,"icons":349},"https:\u002F\u002Frelaxed-cori-9310e4.netlify.app\u002F","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FVue-To-Do-List",[212],[],{"title":352,"excerpt":353,"slug":356,"date":357,"modified":358,"readingTime":77,"acf":359,"tags":363,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"To-Do List",{"raw":354,"plain":355},"\u003Cp>As part of my university course we were tasked with creating a To Do List in Vanilla JavaScript. For many, it was also an introduction into using NPM and BrowserSync for the first time. We also experimented with using Local Browser Storage.\u003C\u002Fp>","As part of my university course we were tasked with creating a To Do List in Vanilla JavaScript. For many, it was also an introduction into using NPM and BrowserSync for the first time. We also experimented with using Local Browser Storage.","to-do-list",["Date","2020-04-24T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":360,"github":361,"icons":362},"https:\u002F\u002Fcodepen.io\u002Fjoebailey26\u002Fpen\u002FwvvbBpN","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FJavaScript-To-Do-List",[155],[],{"title":365,"excerpt":366,"slug":369,"date":370,"modified":371,"readingTime":77,"acf":372,"tags":373,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Re-creating my Portfolio with React",{"raw":367,"plain":368},"\u003Cp>I&#8217;ve just finished recreating my portfolio using React. There aren&#8217;t very many visual changes apart from changing the hover effects a little. The site features a lot of repetitive content, such as portfolio items. React is good for this because I can create a component for the item which minimizes the amount of coding required. [&hellip;]\u003C\u002Fp>","I’ve just finished recreating my portfolio using React. There aren’t very many visual changes apart from changing the hover effects a little. The site features a lot of repetitive content, such as portfolio items. React is good for this because I can create a component for the item which minimizes the amount of coding required. […]","re-creating-my-portfolio-with-react",["Date","2020-02-03T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],[375,388,398,408,418,428,438,450,462,472],{"title":376,"excerpt":377,"slug":380,"date":381,"modified":382,"readingTime":77,"acf":383,"tags":387,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Weather app",{"raw":378,"plain":379},"\u003Cp>My friend made a weather app so that they could learn APIs. I decided to fork it and take it a step further by experimenting with CSS Houdini, Variable Fonts, CSS Variables, and new browser APIs such as the Speech Synthesis API. Features This App uses experimental&nbsp;CSS Houdini&nbsp;features that are currently only supported in Google [&hellip;]\u003C\u002Fp>","My friend made a weather app so that they could learn APIs. I decided to fork it and take it a step further by experimenting with CSS Houdini, Variable Fonts, CSS Variables, and new browser APIs such as the Speech Synthesis API. Features This App uses experimental CSS Houdini features that are currently only supported in Google […]","weather-app",["Date","2020-02-01T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],{"project_url":384,"github":385,"icons":386},"https:\u002F\u002Fjoebailey26.github.io\u002FWeather-App\u002Findex.html","https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002FWeather-App",[155],[],{"title":389,"excerpt":390,"slug":393,"date":394,"modified":395,"readingTime":42,"acf":396,"tags":397,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"A new Portfolio with Vue.js – Joe Bailey Photography",{"raw":391,"plain":392},"\u003Cp>To mark the beginning of my second year at University I thought that I would try learning a new skill, namely Vue.js. I usually struggle to learn something if I don&#8217;t see any real practical application for it, but this time I figured I could turn my photography portfolio into a dynamic Vue.js enhanced page. [&hellip;]\u003C\u002Fp>","To mark the beginning of my second year at University I thought that I would try learning a new skill, namely Vue.js. I usually struggle to learn something if I don’t see any real practical application for it, but this time I figured I could turn my photography portfolio into a dynamic Vue.js enhanced page. […]","a-new-portfolio-with-vue-js",["Date","2019-12-07T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":399,"excerpt":400,"slug":403,"date":404,"modified":405,"readingTime":77,"acf":406,"tags":407,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Cookie Consent – Joe Bailey Photography",{"raw":401,"plain":402},"\u003Cp>Over the past couple of months, I&#8217;ve added some Analytics software to my site such as Google Analytics and Facebook Pixel. As my website serves customers in the EU, the Cookie Law applies. I have used JavaScript to ensure that cookies are not fired until the user has explicitly accepted them on the website. A [&hellip;]\u003C\u002Fp>","Over the past couple of months, I’ve added some Analytics software to my site such as Google Analytics and Facebook Pixel. As my website serves customers in the EU, the Cookie Law applies. I have used JavaScript to ensure that cookies are not fired until the user has explicitly accepted them on the website. A […]","cookie-consent",["Date","2019-12-07T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":409,"excerpt":410,"slug":413,"date":414,"modified":415,"readingTime":42,"acf":416,"tags":417,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Re-doing my portfolio",{"raw":411,"plain":412},"\u003Cp>I built my web design portfolio site over the Christmas of 2018 and decided it needed a bit of a refresh during the summer. I got in touch with the @_tashhockey, the person behind Honest Feedback, to give my website a bit of a review. She made some really good points and brought to light [&hellip;]\u003C\u002Fp>","I built my web design portfolio site over the Christmas of 2018 and decided it needed a bit of a refresh during the summer. I got in touch with the @_tashhockey, the person behind Honest Feedback, to give my website a bit of a review. She made some really good points and brought to light […]","re-doing-my-portfolio",["Date","2019-10-07T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":419,"excerpt":420,"slug":423,"date":424,"modified":425,"readingTime":77,"acf":426,"tags":427,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Animated mobile menu",{"raw":421,"plain":422},"\u003Cp>In quite a few of my projects I&#8217;ve needed a quick and easy way of making a menu which scales onto mobile. I like the style that android apps have; the animated drawer, so I have replicated this. The menu is swipe aware using Hammer.js which means if you swipe right on mobile then the [&hellip;]\u003C\u002Fp>","In quite a few of my projects I’ve needed a quick and easy way of making a menu which scales onto mobile. I like the style that android apps have; the animated drawer, so I have replicated this. The menu is swipe aware using Hammer.js which means if you swipe right on mobile then the […]","animated-mobile-menu",["Date","2019-09-30T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":429,"excerpt":430,"slug":433,"date":434,"modified":435,"readingTime":77,"acf":436,"tags":437,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"No scroll whilst lightbox is open – Joe Bailey Photography",{"raw":431,"plain":432},"\u003Cp>I read the following article by CSS Tricks https:\u002F\u002Fcss-tricks.com\u002Fprevent-page-scrolling-when-a-modal-is-open\u002F and decided that my website shouldn&#8217;t scroll whilst the lightbox is shown, i.e. on the Blog and the Memories pages when an image is clicked. I use the Featherlight plugin on my Blog and the equivalent for the Memories page. I added the following CSS to [&hellip;]\u003C\u002Fp>","I read the following article by CSS Tricks https:\u002F\u002Fcss-tricks.com\u002Fprevent-page-scrolling-when-a-modal-is-open\u002F and decided that my website shouldn’t scroll whilst the lightbox is shown, i.e. on the Blog and the Memories pages when an image is clicked. I use the Featherlight plugin on my Blog and the equivalent for the Memories page. I added the following CSS to […]","no-scroll-whilst-lightbox-is-open",["Date","2019-08-01T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":439,"excerpt":440,"slug":443,"date":444,"modified":445,"readingTime":77,"acf":446,"tags":447,"author":18,"featuredMediaUrl":448,"featuredMedia":449},"Dark Mode – Joe Bailey Photography",{"raw":441,"plain":442},"\u003Cp>I read the following article: https:\u002F\u002Fwww.howtogeek.com\u002F434592\u002Fwhat%E2%80%99s-new-in-chrome-76-arriving-july-30th\u002F and was excited by &#8220;Automatic Dark Mode is Coming to Websites&#8221;. I decided to give it a shot. I read up on the Media Queries Level 5 spec and added the &#8220;prefers-color-scheme&#8221; media query to my CSS. I downloaded a copy of Chrome Canary to test the website, as [&hellip;]\u003C\u002Fp>","I read the following article: https:\u002F\u002Fwww.howtogeek.com\u002F434592\u002Fwhat%E2%80%99s-new-in-chrome-76-arriving-july-30th\u002F and was excited by “Automatic Dark Mode is Coming to Websites”. I decided to give it a shot. I read up on the Media Queries Level 5 spec and added the “prefers-color-scheme” media query to my CSS. I downloaded a copy of Chrome Canary to test the website, as […]","dark-mode",["Date","2019-07-29T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],"https:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F18ef7e8b3ffaa78e6531a8e7db4678f61b89f836e42519209287c63e325266b7\u002F18ef7e8b3ffaa78e-dark-mode.jpg","https:\u002F\u002Fjoebailey.xyz\u002Fcdn-cgi\u002Fimage\u002Fformat=auto,quality=80,fit=scale-down,width=1200\u002Fhttps:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F18ef7e8b3ffaa78e6531a8e7db4678f61b89f836e42519209287c63e325266b7\u002F18ef7e8b3ffaa78e-dark-mode.jpg",{"title":451,"excerpt":452,"slug":455,"date":456,"modified":457,"readingTime":77,"acf":458,"tags":459,"author":18,"featuredMediaUrl":460,"featuredMedia":461},"Joe Bailey Photography is now a Progressive Web App",{"raw":453,"plain":454},"\u003Cp>Making my site work offline had been on my bucket list for a while. It was the last thing in Google Chrome&#8217;s Lighthouse test to complete. The site now has close to 100 scores across all areas in Google Chrome&#8217;s Lighthouse Audit. I read up on Service Workers on both Google Developers and MDN Web [&hellip;]\u003C\u002Fp>","Making my site work offline had been on my bucket list for a while. It was the last thing in Google Chrome’s Lighthouse test to complete. The site now has close to 100 scores across all areas in Google Chrome’s Lighthouse Audit. I read up on Service Workers on both Google Developers and MDN Web […]","joe-bailey-photography-is-now-a-progressive-web-app",["Date","2019-07-18T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],"https:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F5464e472fd92822b761ed6e957448e480b6c12f6291d9ae23f168b154c37439d\u002F5464e472fd92822b-Untitled-1.jpg","https:\u002F\u002Fjoebailey.xyz\u002Fcdn-cgi\u002Fimage\u002Fformat=auto,quality=80,fit=scale-down,width=1200\u002Fhttps:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F5464e472fd92822b761ed6e957448e480b6c12f6291d9ae23f168b154c37439d\u002F5464e472fd92822b-Untitled-1.jpg",{"title":463,"excerpt":464,"slug":467,"date":468,"modified":469,"readingTime":77,"acf":470,"tags":471,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Data Flow Diagrams",{"raw":465,"plain":466},"\u003Cp>When we first started doing Data Flow Diagrams at the start of the semester I thought they were the worst thing in the world. However, as I came to understand them more and start to think about more real-world business problems that I was encountering such as creating a website for my Dad or as [&hellip;]\u003C\u002Fp>","When we first started doing Data Flow Diagrams at the start of the semester I thought they were the worst thing in the world. However, as I came to understand them more and start to think about more real-world business problems that I was encountering such as creating a website for my Dad or as […]","data-flow-diagrams",["Date","2019-04-19T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":322,"excerpt":473,"slug":476,"date":477,"modified":478,"readingTime":77,"acf":479,"tags":480,"author":18,"featuredMediaUrl":19,"featuredMedia":19},{"raw":474,"plain":475},"\u003Cp>During our Web Technologies practical lessons, we have to follow a set of instructions to create a website or simple script. The lecturer has hosted the notes for the unit on Firebase. I&#8217;d looked into Firebase before but couldn&#8217;t understand how to use it. However, when an extended task came up in lesson I gave [&hellip;]\u003C\u002Fp>","During our Web Technologies practical lessons, we have to follow a set of instructions to create a website or simple script. The lecturer has hosted the notes for the unit on Firebase. I’d looked into Firebase before but couldn’t understand how to use it. However, when an extended task came up in lesson I gave […]","firebase",["Date","2019-04-19T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],[482,492,502,512,524,536,546,556,566,576],{"title":483,"excerpt":484,"slug":487,"date":488,"modified":489,"readingTime":77,"acf":490,"tags":491,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Angular, React, Vue, Node",{"raw":485,"plain":486},"\u003Cp>Whilst working on various projects this semester I&#8217;ve encountered the following technologies: Angular, React, Vue, and Node. I&#8217;ve experimented with them to some extent and found Node Package Manager quite useful. Initially, when I first heard about them I thought they were JavaScript Libraries like JQuery, however, they are far from it. Each technology is [&hellip;]\u003C\u002Fp>","Whilst working on various projects this semester I’ve encountered the following technologies: Angular, React, Vue, and Node. I’ve experimented with them to some extent and found Node Package Manager quite useful. Initially, when I first heard about them I thought they were JavaScript Libraries like JQuery, however, they are far from it. Each technology is […]","angular-react-vue-node",["Date","2019-04-19T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":493,"excerpt":494,"slug":497,"date":498,"modified":499,"readingTime":116,"acf":500,"tags":501,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"New Laptop, Deployment Manager, NAS and VPN",{"raw":495,"plain":496},"\u003Cp>I&#8217;d wanted to buy a new laptop for ages as mine was quite heavy and I wanted to take it to lectures, and store it in my bag but couldn&#8217;t. This meant I was looking for something quite light. I also wanted something powerful as I do a lot of media production and web development. [&hellip;]\u003C\u002Fp>","I’d wanted to buy a new laptop for ages as mine was quite heavy and I wanted to take it to lectures, and store it in my bag but couldn’t. This meant I was looking for something quite light. I also wanted something powerful as I do a lot of media production and web development. […]","new-laptop-deployment-manager-nas-and-vpn",["Date","2019-04-16T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":503,"excerpt":504,"slug":507,"date":508,"modified":509,"readingTime":77,"acf":510,"tags":511,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Git",{"raw":505,"plain":506},"\u003Cp>As part of my studies at Solent, we were introduced to using Git in our workflow. Git is useful as it means mistakes can be corrected by rolling back the code to a version that worked. I&#8217;d come across this before at Fluid Studios Ltd. where they were using Git Kraken, however, I&#8217;d never really [&hellip;]\u003C\u002Fp>","As part of my studies at Solent, we were introduced to using Git in our workflow. Git is useful as it means mistakes can be corrected by rolling back the code to a version that worked. I’d come across this before at Fluid Studios Ltd. where they were using Git Kraken, however, I’d never really […]","git",["Date","2019-04-16T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":513,"excerpt":514,"slug":517,"date":518,"modified":519,"readingTime":77,"acf":520,"tags":523,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"No Thumbnails Gallery",{"raw":515,"plain":516},"\u003Cp>When creating my portfolio and memories page I needed a quick and easy way of creating galleries of images without having to code each individual image (there are over 3,000 images!). Also, I needed it without a thumbnail for each image. As far as I can tell there isn&#8217;t an easy option to do that. [&hellip;]\u003C\u002Fp>","When creating my portfolio and memories page I needed a quick and easy way of creating galleries of images without having to code each individual image (there are over 3,000 images!). Also, I needed it without a thumbnail for each image. As far as I can tell there isn’t an easy option to do that. […]","no-thumbnails-gallery",["Date","2019-02-06T00:00:00.000Z"],["Date","2026-11-08T00:00:00.000Z"],{"project_url":15,"github":521,"icons":522},"https:\u002F\u002Fgithub.com\u002Fjoebailey26\u002Fno-thumbnails-gallery",[273],[],{"title":525,"excerpt":526,"slug":529,"date":530,"modified":531,"readingTime":77,"acf":532,"tags":533,"author":18,"featuredMediaUrl":534,"featuredMedia":535},"Web Design Portfolio",{"raw":527,"plain":528},"\u003Cp>I&#8217;d been meaning to update my portfolio site for quite a while as I wasn&#8217;t really happy with the design. The long Christmas break gave me a chance to do just that. I was inspired by this template. In my lectures, we&#8217;d been learning about Figma and UX design. I wanted to use this redesign [&hellip;]\u003C\u002Fp>","I’d been meaning to update my portfolio site for quite a while as I wasn’t really happy with the design. The long Christmas break gave me a chance to do just that. I was inspired by this template. In my lectures, we’d been learning about Figma and UX design. I wanted to use this redesign […]","web-design-portfolio",["Date","2019-02-06T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],"https:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F34a32b062918590e903c5210aa0d081d9960608cd1c70ece44100590aeafc4a6\u002F34a32b062918590e-Logo.png","https:\u002F\u002Fjoebailey.xyz\u002Fcdn-cgi\u002Fimage\u002Fformat=auto,quality=80,fit=scale-down,width=1200\u002Fhttps:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002F34a32b062918590e903c5210aa0d081d9960608cd1c70ece44100590aeafc4a6\u002F34a32b062918590e-Logo.png",{"title":537,"excerpt":538,"slug":541,"date":542,"modified":543,"readingTime":77,"acf":544,"tags":545,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Transferring my hosting package from 1and1 IONOS to iPage",{"raw":539,"plain":540},"\u003Cp>I think transferring hosting provider was the single greatest nightmare I&#8217;ve had in my life. I bought a few domains with 1and1, hoping to get SSL certificates that were advertised, however you only get one SSL certificate per contract. I&#8217;d have to create multiple contracts for all my domains, and then would not be able [&hellip;]\u003C\u002Fp>","I think transferring hosting provider was the single greatest nightmare I’ve had in my life. I bought a few domains with 1and1, hoping to get SSL certificates that were advertised, however you only get one SSL certificate per contract. I’d have to create multiple contracts for all my domains, and then would not be able […]","transferring-my-hosting-package-from-1and1-ionos-to-ipage",["Date","2019-02-06T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":547,"excerpt":548,"slug":551,"date":552,"modified":553,"readingTime":77,"acf":554,"tags":555,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Website Updates: Portfolio and Memories – Joe Bailey Photography",{"raw":549,"plain":550},"\u003Cp>After creating my Web Design portfolio site and transferring my hosting package I was in the mood to do some web design. I&#8217;d been meaning to add a portfolio to my photography site for a long while. Whilst making the photography portfolio I created some cool code to create a gallery of images which you [&hellip;]\u003C\u002Fp>","After creating my Web Design portfolio site and transferring my hosting package I was in the mood to do some web design. I’d been meaning to add a portfolio to my photography site for a long while. Whilst making the photography portfolio I created some cool code to create a gallery of images which you […]","website-updates-portfolio-and-memories",["Date","2019-02-01T00:00:00.000Z"],["Date","2026-11-03T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":557,"excerpt":558,"slug":561,"date":562,"modified":563,"readingTime":77,"acf":564,"tags":565,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Don’t forget your future self – Dan Benham",{"raw":559,"plain":560},"\u003Cp>I attended a talk by Dan Benham, set up by the university, which I found really interesting. He works for Studio Republic in Winchester. He was tired of working for corporate clients and started an ethical business to combat this. His business helps charities and other ethical entities with their creative tasks. An interesting question [&hellip;]\u003C\u002Fp>","I attended a talk by Dan Benham, set up by the university, which I found really interesting. He works for Studio Republic in Winchester. He was tired of working for corporate clients and started an ethical business to combat this. His business helps charities and other ethical entities with their creative tasks. An interesting question […]","dont-forget-your-future-self-dan-benham",["Date","2019-01-31T00:00:00.000Z"],["Date","2026-11-03T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":567,"excerpt":568,"slug":571,"date":572,"modified":573,"readingTime":116,"acf":574,"tags":575,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Web Components using PHP – Joe Bailey Photography",{"raw":569,"plain":570},"\u003Cp>In my Web Design and Development course, we touched on using web components and live style guides. This is the practice of having a set of components and styles that can be reused across your website. The advantage is that when something gets updated it rolls out across the whole site at the same time. [&hellip;]\u003C\u002Fp>","In my Web Design and Development course, we touched on using web components and live style guides. This is the practice of having a set of components and styles that can be reused across your website. The advantage is that when something gets updated it rolls out across the whole site at the same time. […]","web-components-using-php",["Date","2018-11-22T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":577,"excerpt":578,"slug":581,"date":582,"modified":583,"readingTime":77,"acf":584,"tags":585,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Wix Vs WordPress",{"raw":579,"plain":580},"\u003Cp>Now I&#8217;ve come across two instances of Wix in the past couple of months. Neither website was optimised for mobile. Wix has the option there, but it&#8217;s incredibly hard to use, you can change one thing and it will delete half the content on your site. The drag and drop editor is extremely slow, it&#8217;s [&hellip;]\u003C\u002Fp>","Now I’ve come across two instances of Wix in the past couple of months. Neither website was optimised for mobile. Wix has the option there, but it’s incredibly hard to use, you can change one thing and it will delete half the content on your site. The drag and drop editor is extremely slow, it’s […]","wix-vs-wordpress",["Date","2018-11-22T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],[587,597,609,619,629,639,649,659,673,683],{"title":588,"excerpt":589,"slug":592,"date":593,"modified":594,"readingTime":42,"acf":595,"tags":596,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Flickr changes to free accounts – Joe Bailey Photography",{"raw":590,"plain":591},"\u003Cp>You might notice that my website uses Flickr to display most of the images. This is done by using an iframe of the Flickr slideshow. The reason I chose to use Flickr to display my images is that I didn&#8217;t have much experience coding at the time (I&#8217;m still not a code genius). It was [&hellip;]\u003C\u002Fp>","You might notice that my website uses Flickr to display most of the images. This is done by using an iframe of the Flickr slideshow. The reason I chose to use Flickr to display my images is that I didn’t have much experience coding at the time (I’m still not a code genius). It was […]","flickr-changes-to-free-accounts",["Date","2018-11-22T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":598,"excerpt":599,"slug":602,"date":603,"modified":604,"readingTime":77,"acf":605,"tags":606,"author":18,"featuredMediaUrl":607,"featuredMedia":608},"Logo design for a Web Developer",{"raw":600,"plain":601},"\u003Cp>My first project at university was to write a report on how I designed a set of logos to brand myself as a web developer. We had to include research, feedback and final designs. I got a C1 on this report as I found it quite hard being the first one I&#8217;ve done at university. [&hellip;]\u003C\u002Fp>","My first project at university was to write a report on how I designed a set of logos to brand myself as a web developer. We had to include research, feedback and final designs. I got a C1 on this report as I found it quite hard being the first one I’ve done at university. […]","logo-design-for-a-web-developer",["Date","2018-11-09T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],"https:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002Fbfc46bfee4b29cd76ba47fce9df8298461166dcf1625803ae0fda5cf85803866\u002Fbfc46bfee4b29cd7-1Artboard-1joe.png","https:\u002F\u002Fjoebailey.xyz\u002Fcdn-cgi\u002Fimage\u002Fformat=auto,quality=80,fit=scale-down,width=1200\u002Fhttps:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002Fbfc46bfee4b29cd76ba47fce9df8298461166dcf1625803ae0fda5cf85803866\u002Fbfc46bfee4b29cd7-1Artboard-1joe.png",{"title":610,"excerpt":611,"slug":614,"date":615,"modified":616,"readingTime":77,"acf":617,"tags":618,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"rankingCoach",{"raw":612,"plain":613},"\u003Cp>I attended a workshop in Hotel Mercure Southampton Centre Dolphin on rankingCoach by Alasdair Stone. It was a very interesting event and we learnt about how to optimize website for search engines. We also learnt how to use tools such as Google My Business. I came away from the event with a lot of notes [&hellip;]\u003C\u002Fp>","I attended a workshop in Hotel Mercure Southampton Centre Dolphin on rankingCoach by Alasdair Stone. It was a very interesting event and we learnt about how to optimize website for search engines. We also learnt how to use tools such as Google My Business. I came away from the event with a lot of notes […]","rankingcoach",["Date","2018-11-08T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":620,"excerpt":621,"slug":624,"date":625,"modified":626,"readingTime":42,"acf":627,"tags":628,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Material Design",{"raw":622,"plain":623},"\u003Cp>“Make beautiful products, faster. Material is a design system – backed by open-source code – that helps teams build digital experiences” Material.io Material is a design language created by Google in 2014 for the Android platform. It has unified Android apps under a single design format. This has helped to give the Android platform a [&hellip;]\u003C\u002Fp>","“Make beautiful products, faster. Material is a design system – backed by open-source code – that helps teams build digital experiences” Material.io Material is a design language created by Google in 2014 for the Android platform. It has unified Android apps under a single design format. This has helped to give the Android platform a […]","material-design",["Date","2018-10-17T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":630,"excerpt":631,"slug":634,"date":635,"modified":636,"readingTime":77,"acf":637,"tags":638,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Barcamp Southampton",{"raw":632,"plain":633},"\u003Cp>I went to a Barcamp in Southampton, which consists of people giving talks on things they find interesting, with a heavy bias on technology. The normal talks last 45 minutes to an hour. They also have an opportunity to give lightning talks after lunch which last about 10 minutes and can be about anything. I [&hellip;]\u003C\u002Fp>","I went to a Barcamp in Southampton, which consists of people giving talks on things they find interesting, with a heavy bias on technology. The normal talks last 45 minutes to an hour. They also have an opportunity to give lightning talks after lunch which last about 10 minutes and can be about anything. I […]","barcamp-soton",["Date","2018-10-07T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":640,"excerpt":641,"slug":644,"date":645,"modified":646,"readingTime":42,"acf":647,"tags":648,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"CSS Grid",{"raw":642,"plain":643},"\u003Cp>&#8220;CSS Grid Layout is shaping up to be the layout tool we’ve always wanted on the web.&#8221; Stacy Kvernmo (September 19, 2016) In the early days of the web, developers laid things out using tables. Tables are only designed for tabular data and developers were splitting GIFs up to lay them out and create visually [&hellip;]\u003C\u002Fp>","“CSS Grid Layout is shaping up to be the layout tool we’ve always wanted on the web.” Stacy Kvernmo (September 19, 2016) In the early days of the web, developers laid things out using tables. Tables are only designed for tabular data and developers were splitting GIFs up to lay them out and create visually […]","css-grid",["Date","2018-10-04T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":650,"excerpt":651,"slug":654,"date":655,"modified":656,"readingTime":77,"acf":657,"tags":658,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Web Design and Development",{"raw":652,"plain":653},"\u003Cp>I&#8217;ve just started my first proper job in the Web Design and Development industry at Fluid Studios Ltd&nbsp;and it&#8217;s going really well. I&#8217;m also starting to get ready for university, the next big exciting chapter in my life.\u003C\u002Fp>","I’ve just started my first proper job in the Web Design and Development industry at Fluid Studios Ltd and it’s going really well. I’m also starting to get ready for university, the next big exciting chapter in my life.","web-design-and-development",["Date","2018-06-08T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":660,"excerpt":661,"slug":664,"date":665,"modified":666,"readingTime":77,"acf":667,"tags":670,"author":18,"featuredMediaUrl":671,"featuredMedia":672},"joebailey.xyz",{"raw":662,"plain":663},"\u003Cp>I&#8217;ve started creating a site to showcase my work as a web design professional. Once finished it will be live at joebailey.xyz I&#8217;m not really feeling it as much as my photography website, I don&#8217;t think the design flows that well, and it doesn&#8217;t really optimize for mobile all that well either. I think maybe [&hellip;]\u003C\u002Fp>","I’ve started creating a site to showcase my work as a web design professional. Once finished it will be live at joebailey.xyz I’m not really feeling it as much as my photography website, I don’t think the design flows that well, and it doesn’t really optimize for mobile all that well either. I think maybe […]","joebailey-xyz",["Date","2018-03-21T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":668},[336,155,669],"Netlify",[],"https:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002Fe28b08c407668af2eb67e835a3986a145fd2c94feace17010d6a8d860d780278\u002Fe28b08c407668af2-Logo.jpg","https:\u002F\u002Fjoebailey.xyz\u002Fcdn-cgi\u002Fimage\u002Fformat=auto,quality=80,fit=scale-down,width=1200\u002Fhttps:\u002F\u002Fcms.joebailey.xyz\u002Fmedia\u002Fe28b08c407668af2eb67e835a3986a145fd2c94feace17010d6a8d860d780278\u002Fe28b08c407668af2-Logo.jpg",{"title":674,"excerpt":675,"slug":678,"date":679,"modified":680,"readingTime":77,"acf":681,"tags":682,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"New Blog – Joe Bailey Photography",{"raw":676,"plain":677},"\u003Cp>I&#8217;ve finally started phase 3 of the redesign of my website. Phase one was to create it, learning lots of code along the way to prepare me for my University course, and also to give me the creative freedom that I hadn&#8217;t yet been able to achieve with various website builders. During phase 2 I [&hellip;]\u003C\u002Fp>","I’ve finally started phase 3 of the redesign of my website. Phase one was to create it, learning lots of code along the way to prepare me for my University course, and also to give me the creative freedom that I hadn’t yet been able to achieve with various website builders. During phase 2 I […]","new-blog",["Date","2018-03-21T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],{"title":684,"excerpt":685,"slug":688,"date":689,"modified":690,"readingTime":77,"acf":691,"tags":692,"author":18,"featuredMediaUrl":19,"featuredMedia":19},"Welcome – Joe Bailey Photography",{"raw":686,"plain":687},"\u003Cp>Hello everyone, Welcome to my new website. Sorry for the delay, it&#8217;s been a year in the making and there&#8217;s still much to do, like implement a store and add my backlog of images to the gallery. If you encounter any problems whilst browsing, please don&#8217;t hesitate to contact me, and I&#8217;ll see what I [&hellip;]\u003C\u002Fp>","Hello everyone, Welcome to my new website. Sorry for the delay, it’s been a year in the making and there’s still much to do, like implement a store and add my backlog of images to the gallery. If you encounter any problems whilst browsing, please don’t hesitate to contact me, and I’ll see what I […]","welcome",["Date","2018-01-20T00:00:00.000Z"],["Date","2026-10-22T00:00:00.000Z"],{"project_url":15,"github":15,"icons":15},[],1794151846395]