DEV Community

Jan Friebe
Jan Friebe

Posted on • Updated on

Using Astro Image Optimization Benefits with Tina CMS Cloud in Production build

Problem Statement

In production, the benefits of image optimization using Astro's Image/Picture component are lost when integrating with Tina CMS and Tina Cloud. However, I have found a workaround to resolve this issue. Whether this workaround is the best or a good fit for client websites remains uncertain, but here are the details. For further context, you can read the github discussion here.

Preface

Typically, you have a markdown file with an image path pointing to a locally based image, like so:

imgSrc: /src/assets/img/logo.png
...
Enter fullscreen mode Exit fullscreen mode

To leverage the image optimization benefits of the Astro Image component, such as generating multiple versions of an image based on device density, you would use dynamic image imports as described in astro docs. Everything works perfectly in development mode when you run npm run "tinacms dev -c \"astro dev\"",. Tina CLI commands But for clarity here an example of importing images dynamically.

---
import type { ImageMetadata } from "astro";
import { Image } from "astro:assets";

interface Props {
  imgSrc: string;
}

const { imgSrc } = Astro.props;

const images = import.meta.glob<{ default: ImageMetadata }>("/src/assets/img/*.{jpeg,jpg,png,gif}");

if (!images[imgSrc]) {
  throw new Error(`"${imgSrc}" does not exist in glob: "src/assets/img/*.{jpeg,jpg,png,gif}"`);
}

---

<Image
  width="70"
  height="70"
  src={images[imgSrc]()}
  alt="test image"
  densities={[1.5, 2]}
  loading="lazy"
/>

Enter fullscreen mode Exit fullscreen mode

The Production Issue

In production, Tina CMS typically retrieves its media from its own CDN server (e.g., https://assets.tina.io/image). This setup conflicts with Astro's automatic image optimization and the associated glob pattern.

The function tries to search for the image in the local path. This works in development mode, but in production, the imgSrc returned from the markdown file is no longer a local string. Instead, it returns a URL like https://assets.tina.io/image, causing the search function aka glob function to fail and resulting in a build error.

For more information, refer to the github discussion here.

Workaround for Using Image Optimization

To still leverage Astro's image optimization in production, you need to adapt the build script:

Modified Build Script

The build script must be updated to:

"scripts": {
  "build": "tinacms build --local -c \"astro build\""
}
Enter fullscreen mode Exit fullscreen mode

However, be aware that dynamically fetching data will no longer work as expected. For example, querying dynamic data using Tina's client in for example a Date component will not working properly.

Example script of my dynamically data fetches

<script>
        import { formatMarkdownUrl, formatDateString } from "../utils/format";

        document.addEventListener("DOMContentLoaded", async () => {
            try {
                const { client } = await import(
                    "../../tina/__generated__/client"
                );

                async function getLiveDatesResponse() {
                    try {
                        const response =
                            await client.queries.live_dateConnection({
                                sort: "date",
                            });
                        return response;
                    } catch (error) {
                        console.error(
                            error,
                        );
                        return null;
                    }
                }

            }
        })
</script>
Enter fullscreen mode Exit fullscreen mode

Conclusion

This workaround allows you to leverage Astro's image optimization benefits in production while using Tina CMS and Tina Cloud. However, it introduces limitations in dynamically fetching data, which may not be suitable for all use cases. Evaluate whether this approach fits your project's requirements and constraints.

For further reading and to join the discussion, check out the GitHub discussion.

Top comments (1)

Collapse
 
friebe profile image
Jan Friebe

A short update on my situation.
We all know it, you sit in front of a problem, ponder and try around and the solution is so simple, only you just don't see it -.-

It helped me to reread my own written article. In sentence

The function tries to search for the image in the local path. This works in development mode, but in production, the imgSrc returned from the markdown file is no longer a local string. Instead, it returns a URL like assets.tina.io/image...

I indirectly presented myself with a solution.

A simple if condition to check whether the images should be used locally or from the Tina CDN is sufficient here. Here is my code:

function beginsWithTinaAssets(imgSrc: string): boolean {
    const prefix = "https://assets.tina.io";
    return imgSrc.startsWith(prefix);
}

function extractFileName(filePath: string): string {
    const lastSlashIndex = filePath.lastIndexOf("/");
    return filePath.substring(lastSlashIndex + 1);
}

if (beginsWithTinaAssets(imgSrc)) {
    const filename = extractFileName(imgSrc);
    imgSrc = `/src/assets/img/${filename}`;
}
Enter fullscreen mode Exit fullscreen mode

With this solution I can now use optimized images as well as the dynamic query via the graphql server as in my example in the date component.

Whether it is a legitimate way I can not answer, it feels like a workaround that works for my purposes so far