↖ Writing
polaroid of an orange lama next to a small rocket

Image generated with a machine learning model.

How to use Astro’s Image Optimization with Tina CMS

How to use Astro’s Image Optimization with Tina CMS

Note: This guide was originally written for Astro version 2.x. If you are using Astro 3.x, you can jump straight to the Update for Astro 3.0 section; or read ahead for context.

Astro Assets (“Astro Images” in v3) is a powerful image optimisation tool built into Astro. It is experimental in Astro 2.x, but stable in Astro 3.x.

By default, the path syntax that Tina CMS uses to reference images is incompatible with Astro Asset’s syntax. However, we can make those two tools work together by adding a prebuild script.

The problem: Different path syntax

In order for Astro Assets to process a file, it needs to be

  1. Placed in the /src/assets folder
  2. Referenced with a path alias1 like this: ~/src/assets.

In a markdown file, this is the syntax for an image to be optimised by Astro:

![alt text](~/assets/image.png)

However, Tina cannot resolve Astro’s path alias. Therefore images are broken:

Screenshot of the TinaCMS interface. Instead of images, a broken image icon is displayed.

We could switch all images to the Tina-compatible syntax:

![alt text](/src/assets/image.png)

But then we loose Astro Asset’s image optimisation.

Solution: Use a Tina-friendly path and adapt it on build

Step 1: Change the Tina media folder

At the time of writing, the default path for media in TinaCMS is /public. Change the path to /src/assets in the Tina config:

// tina/config.ts

export default defineConfig({
  media: {
    tina: {
      mediaRoot: "/src/assets",
      publicFolder: "",
    },
  },
  {
    schema: {
      // other configuration…
    }
  },
});

TinaCMS now places images in the /src/assets folder.

Step 2: Add a script that adapts the path on build

When we edit our site locally, we leave the image paths in the Tina-friendly /src/assets syntax. But when we build our site on a server, we run a script that replaces the path with the Astro Assets-friendly ~/src/assets path alias.

Add a prebuild script to package.json:

{
  "scripts": {
    "dev": "tinacms dev -c 'astro dev'",
    "prebuild": "node scripts/replaceAssetPaths.js",
    "build": "astro build"
  }
}

At the root of the project, create a scripts folder and create the file replaceAssetPaths.js in it.

This script will find all occurrences of /src/assets/ and replace it with ~/src/assets/:

// /scripts/replaceAssetPaths.js

// all .md files in this directory and subdirectories will be processed
const postsDirectory = "/src/content/posts/";

// Find and replace strings
const find = "/src/assets/";
const replace = "~/assets/";
// Special characters (https://en.wikipedia.org/wiki/Regular_expression#POSIX_basic_and_extended) need to be escaped

import { exec } from "child_process";
// execute bash command
exec(
  `find ${process.cwd()}${postsDirectory} -type f -name '*.md' -print0 | xargs -0 sed -i -e 's:${find}:${replace}:g'`,
  // Note: GNU sed that runs on Linux but not on mac (most servers use Linux)
  (error, stdout, stderr) => {
    // error handling
    if (error) {
      console.log(`error: ${error.message}`);
      return;
    }
    if (stderr) {
      console.log(`stderr: ${stderr}`);
      return;
    }
    // success
    console.log(stdout);
    console.log("🖼️ Successfully replaced asset paths");
  }
);

Running this locally, you might run into a gotcha with the sed command. Linux uses sed -i (GNU); MacOS uses sed -i '' (BSD). So the script might not work locally on your Mac. But it runs on the (likely) Linux-based server which hosts your website2.

TinaCMS & Astro Assets: The best of both worlds!

When writing locally, you can edit your website in TinaCMS, adding images to your heart’s delight!

On deploy, npm runs prebuild, which executes replaceAssetPaths.js. After that, build runs with Astro Assets-compatible paths. Once the site is built, you can inspect any images on the site to check that they were processed correctly.

Now have a website with the best of both worlds: Tina’s simple CMS experience and Astro Assets’ powerful image optimisation.

Update for Astro 3.0

In Astro 3.0, support for path aliases (eg. ~/assets) has been removed. Images need to be referenced with relative paths instead (eg. ../../assets).

In a nutshell, these are the steps to use Astro’s Image Optimisation with a CMS provider like TinaCMS:

  1. Change the CMS media folder
  2. Add a prebuild script in package.json
  3. Create the script file (see below)
// scripts/replaceAssetPaths.js

// all .md files in this directory will be processed
// specify from the root of your project
const postsDirectory = "/src/content/posts/";

// Find and replace strings
const find = "/src/assets/";
let replace = "../../../../../assets/"; // update this path based on where files are located

replace = replace.replaceAll(".", "\\.");
// Special characters (https://en.wikipedia.org/wiki/Regular_expression#POSIX_basic_and_extended) need to be escaped

import { exec } from "child_process";
// execute bash command
exec(
  `find ${process.cwd()}${postsDirectory} -type f -name '*.md' -print0 | xargs -0 sed -i -e 's:${find}:${replace}:g'`,
  // GNU sed that runs on Linux but not on mac
  (error, stdout, stderr) => {
    // error handling
    if (error) {
      console.log(`error: ${error.message}`);
      return;
    }
    if (stderr) {
      console.log(`stderr: ${stderr}`);
      return;
    }
    // success
    console.log(stdout);
    console.log("🖼️ Successfully replaced asset paths");
  }
);

Footnotes

  1. Images can also be referenced by relative path: ../../../assets/image.png. But I prefer the alias syntax since it is more concise.

  2. I tried using perl to have a macOS and Linux compatible version. I couldn’t get it to work. If you know how to construct a version that works on Linux and MacOS, please let me know and I will update the blog post!