Streamlining My Blog Publishing Workflow: A Journey in Automation and Optimization
Over the past few weeks, I’ve been focusing on improving my blog publishing workflow. As someone who values both efficiency and quality, I wanted to create a system that allowed me to publish engaging, media-rich content quickly and without sacrificing performance. With some strategic automation, tools, and thoughtful planning, I’ve managed to build a workflow that does just that.
1. Transition to MDX for Flexible Content
One of the biggest upgrades to my workflow was transitioning from using markdown and Astro files to MDX (Markdown + JSX). This switch was a game changer because it allowed me to combine the simplicity of markdown with the power of reusable components. I no longer had to choose between writing content quickly or adding custom elements and media to my posts—MDX allowed me to do both effortlessly.
For instance, I’ve built a responsive image component that works beautifully across different device sizes and uses the AVIF format for optimized performance. Now, whether someone reads my blog on desktop, mobile, or an RSS feed, they get the best possible experience, complete with images that load fast and look sharp.
2. Automating Image Conversion to AVIF
Speaking of images, my blog heavily relies on visuals, so I knew optimizing them was key. With the AVIF format offering significantly smaller file sizes without sacrificing quality, I automated the process of converting my images to AVIF. This wasn’t just a one-off task; we developed a system where I can batch convert images, generate missing formats, and even sync everything with my AWS S3 bucket for seamless delivery.
By integrating Automator scripts, I can now handle repetitive tasks—like converting images or syncing files to AWS—with a simple right-click in Finder or running a Quick Action. This dramatically reduces the time spent on manual tasks and keeps my local files and AWS storage neatly in sync.
3. Syncing Local Files to AWS
Keeping my local image library in sync with my AWS S3 storage was a headache I was determined to fix. We’ve set up a series of automated syncing scripts for both uploading new files and downloading existing ones, with dry runs to ensure nothing is accidentally overwritten. These scripts have been a lifesaver, especially with the exclusion of .DS_Store files and the flexibility to add, delete, or modify files and folders both locally and in the cloud.
4. Making the Publishing Process Smoother
While much of the focus has been on images and media optimization, we also explored how to streamline the publishing process itself. By implementing automation and expanding the use of MDX, I’ve made it easier to manage my posts, tag and categorize content, and even prepare reusable components for common elements in my writing.
Some ideas I’m considering for further optimization include setting up automated performance testing and content analytics for deeper insights into how my readers engage with my posts.
5. What’s Next?
I’ve already come a long way in optimizing my blog workflow, but there’s still more to explore. Next on the agenda is experimenting with more interactive content, fine-tuning SEO, and even documenting this workflow for others to learn from. Who knows, maybe I’ll turn this journey into a series of posts that could help other bloggers improve their own workflows.
Stay tuned!
Here’s a more detailed section on the responsive image component, including code samples:
2. Automating Image Conversion and Responsive Images with AVIF
One of the most important upgrades to my workflow was optimizing how I handle images, particularly how they’re displayed across different devices. I knew that to give readers the best experience—whether they were on a desktop, mobile, or reading my content through an RSS feed—I needed a smart, responsive solution that would adjust the image format and size dynamically.
The Responsive Image Component
To achieve this, I created a responsive image component using Astro and MDX. This component allows me to specify different image formats for various device sizes and choose an optimal format, like AVIF, for browsers that support it. The flexibility of this component has been a game changer for how I manage and display media on my site.
Here’s what the final version of the component looks like:
---
interface Props {
verticalImage?: string;
squareImage?: string;
horizontalImage?: string;
alt?: string;
figCaption?: string;
pathPrefix: string;
figureClass?: string;
}
const { verticalImage, squareImage, horizontalImage, alt, figCaption, pathPrefix, figureClass } = Astro.props;
const baseURL = "https://images.alabut.com/writing/";
---
<figure class={figureClass}>
<picture>
{verticalImage && horizontalImage && squareImage && (
<source type="image/avif" media="(max-width: 767px)" srcset={`${baseURL}${pathPrefix}${verticalImage.replace(/\.\w+$/, '.avif')}`} />
<source type="image/avif" media="(min-width: 768px)" srcset={`${baseURL}${pathPrefix}${horizontalImage.replace(/\.\w+$/, '.avif')}`} />
<source media="(max-width: 767px)" srcset={`${baseURL}${pathPrefix}${verticalImage}`} />
<source media="(min-width: 768px)" srcset={`${baseURL}${pathPrefix}${horizontalImage}`} />
<img decoding="async" loading="lazy" alt={alt || ""} src={`${baseURL}${pathPrefix}${squareImage}`} />
)}
{verticalImage && squareImage && !horizontalImage && (
<source type="image/avif" media="(max-width: 767px)" srcset={`${baseURL}${pathPrefix}${verticalImage.replace(/\.\w+$/, '.avif')}`} />
<source media="(max-width: 767px)" srcset={`${baseURL}${pathPrefix}${verticalImage}`} />
<img decoding="async" loading="lazy" alt={alt || ""} src={`${baseURL}${pathPrefix}${squareImage}`} />
)}
{verticalImage && horizontalImage && !squareImage && (
<source type="image/avif" media="(max-width: 767px)" srcset={`${baseURL}${pathPrefix}${verticalImage.replace(/\.\w+$/, '.avif')}`} />
<source type="image/avif" media="(min-width: 768px)" srcset={`${baseURL}${pathPrefix}${horizontalImage.replace(/\.\w+$/, '.avif')}`} />
<source media="(max-width: 767px)" srcset={`${baseURL}${pathPrefix}${verticalImage}`} />
<source media="(min-width: 768px)" srcset={`${baseURL}${pathPrefix}${horizontalImage}`} />
<img decoding="async" loading="lazy" alt={alt || ""} src={`${baseURL}${pathPrefix}${horizontalImage}`} />
)}
{squareImage && horizontalImage && !verticalImage && (
<source type="image/avif" media="(min-width: 768px)" srcset={`${baseURL}${pathPrefix}${horizontalImage.replace(/\.\w+$/, '.avif')}`} />
<source media="(min-width: 768px)" srcset={`${baseURL}${pathPrefix}${horizontalImage}`} />
<img decoding="async" loading="lazy" alt={alt || ""} src={`${baseURL}${pathPrefix}${squareImage}`} />
)}
{verticalImage && !squareImage && !horizontalImage && (
<source type="image/avif" srcset={`${baseURL}${pathPrefix}${verticalImage.replace(/\.\w+$/, '.avif')}`} />
<img decoding="async" loading="lazy" alt={alt || ""} src={`${baseURL}${pathPrefix}${verticalImage}`} />
)}
{horizontalImage && !verticalImage && !squareImage && (
<source type="image/avif" srcset={`${baseURL}${pathPrefix}${horizontalImage.replace(/\.\w+$/, '.avif')}`} />
<img decoding="async" loading="lazy" alt={alt || ""} src={`${baseURL}${pathPrefix}${horizontalImage}`} />
)}
{squareImage && !verticalImage && !horizontalImage && (
<source type="image/avif" srcset={`${baseURL}${pathPrefix}${squareImage.replace(/\.\w+$/, '.avif')}`} />
<img decoding="async" loading="lazy" alt={alt || ""} src={`${baseURL}${pathPrefix}${squareImage}`} />
)}
</picture>
{figCaption && <figcaption>{figCaption}</figcaption>}
</figure>
Why This Matters
This component serves images in the most appropriate format and size based on the user’s device. If the user is on mobile, it serves vertical images; on desktop, it defaults to horizontal or square images. And in cases where AVIF is supported, it ensures that the lightweight, high-performance AVIF format is delivered.
With this setup:
- Square images are the default for the
<img>tag, which is especially useful for RSS readers that don’t support the<picture>element. - Widescreen or vertical images are served depending on the user’s device, ensuring the best visual experience on both mobile and desktop.
- AVIF is used where supported, providing better compression and smaller file sizes than JPG or PNG.
Example of Usage in MDX
Here’s an example of how I use the ResponsiveImage component in an MDX post:
<ResponsiveImage
pathPrefix={frontmatter.pathPrefix}
verticalImage="fisherman1-vertical.jpg"
squareImage="fisherman1-square.jpg"
alt="Fisherman in the San Diego River"
figCaption="Fisherman in the San Diego River"
/>
Which renders this image:
Note the lack of a horizontal image – it wouldn’t look good for this subject matter, which is often the case when the original image is vertical – so it toggles between vertical and square instead.
This allows me to quickly reference the same image in multiple formats (vertical, square, horizontal), and thanks to the component’s logic, the most appropriate image is shown depending on the user’s device.
Automated AVIF Conversion
To make sure all my images are optimized, I automated the conversion to AVIF using ImageMagick. I now have an Automator script set up on my Mac that allows me to select multiple images, right-click, and automatically convert them to AVIF format, streamlining this process even further. This has cut down my image sizes by up to 80%, making the blog load faster without any noticeable loss in quality.
The command I use for batch conversion is:
for file in *.jpg; do magick "$file" -quality 80 "${file%.*}.avif"; done
This one-liner converts all .jpg files in a folder to .avif while maintaining a high quality setting of 80%.
This workflow upgrade has significantly improved the performance of my site, ensuring that images are fast-loading and optimized for every user, no matter their device or network speed.