Thinkmill / keystatic

First class CMS experience, TypeScript API, Markdown & YAML/JSON based, no DB
https://keystatic.com
MIT License
1.14k stars 76 forks source link

Astro images relative paths with nested content #930

Open Matthijz98 opened 7 months ago

Matthijz98 commented 7 months ago

I am trying to add Keystatic to a few Astro sites I manage. But there is one problem I just can not seem to fix asked in the Discord but tought this is a beter place because it is not a config error as far as i know.

I really like and depend on the Astro image component. But in order for the Astro image component to work the imports of the images need to be relative. And after doing some research that can work in Keystatic as well

With a config something like this:

images: {
  directory: 'src/content/kvb1/images',
  publicPath: '../../content/kvb1/images/',
},

I have content collections data like this: image When adding a image to the intro.mdoc the mdoc file in the root of the content collections it work fine without any problem.

image Also Keystatic work fine.

But when trying to add a image to a mdoc file inside a nested folder the uploading works fine image The mdoc file show the images added as:

![](../../content/kvb1/images/techniek/motor-en-aandrijving/binnenboordmotor/thumb.jpg)

but for it to work i should be:

![](../../../../content/kvb1/images/techniek/motor-en-aandrijving/binnenboordmotor/thumb.jpg)

But now Keystatic does not work anymore

Also:

Matthijz98 commented 7 months ago

I hacked my way around it after watching this stream. Still has some problems but seams to work for now:

My key static config:

content: fields.document({
    images: {
        directory: 'src/assets/kvb1/images/',
        publicPath: '/assets/kvb1/images/',
    },
}),

When saving a image with key static it is stored in the assets folder. The markdoc image is added like this:

![](/assets/kvb1/images/intro/KWM11521.jpg)

Then to get the astro image component working i made a custom markdoc image component:

---
import type {ImageMetadata} from 'astro';
const {src, alt, title, __optimizedSrc} = Astro.props
import {Image} from 'astro:assets';

const fixedSrc = '/src' + src // append /src/ because the glob import works from project root

const images = import.meta.glob<{ default: ImageMetadata }>('/src/assets/**/*.{jpeg,jpg,png,gif}')  // use /**/* so it finds nested images

if (!images[fixedSrc]) throw new Error(`"${fixedSrc}" does not exist in glob: "src/assets/**.{jpeg,jpg,png,gif}"`);
---
<figure class="w-fit">
    <Image src={images[fixedSrc]()} alt={alt} class="rounded"
           widths={[800, 1600]}
           sizes={`(max-width: 720px) 800px, (max-width: 1600px) 1600px, ${images[fixedSrc]().width}px`}/>
    <figcaption class="text-center">{title}</figcaption>
</figure>

And then this custom image component is added to the mdoc config

nodes: {
        image:{
            ...nodes.image,
            render: component('./src/components/mdoc/Image.astro'),
            attributes: {
                src: { type: String },
                alt: { type: String },
                title: { type: String },
            },
        },

Some problems this solution still has:

simonswiss commented 7 months ago

Hey friends!

After asking the question about nested collections and assets in the Astro Discord, I was pointed to this solution:

Define a path alias in your tsconfig.json like so:

{
  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "react",
+    "baseUrl": ".",
+    "paths": {
+      "@assets/*": ["src/assets/*"]
+    }
  }
}

...and then, you can set the publicPath to @assets/... for images, and... it will work!

image: fields.object({
  file: fields.image({
    label: 'Image',
    directory: 'src/assets/images',
    publicPath: '@assets/images',
  }),
}),

This will bypass the problem of having to work out how to construct ../../../ segments based on the slug nesting 🔥