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
- Placed in the
/src/assets
folder - 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:
We could switch all images to the Tina-compatible syntax:
![alt text](/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:
- Change the CMS media folder
- Add a
prebuild
script inpackage.json
- 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
- Images can also be referenced by relative path:
../../../assets/image.png
. But I prefer the alias syntax since it is more concise. ↩ - 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! ↩