keystatic icon indicating copy to clipboard operation
keystatic copied to clipboard

Trying to preview a componentBlock image in the editor

Open mercury7design opened this issue 9 months ago • 1 comments

Hey, Im having a hard time figuring this one out!

I'm using Astro. I've created two component blocks that I want to use in blog posts: Youtube Videos, and images. I've been able to preview the youtube videos in my editor without issue, with the help of this video.

But for the life of me, I don't know how to render a preview of the image as well. I've changed my config file from TS to TSX, imported React, etc... and I just can't get a dang image to preview.

Digging through the KeyStatic Docs, I think this organization principle might be relevant...

Regardless of where the posts entries are created, the coverImage image will be generated in public/images/posts/{post-slug}.

My best guess is that, since images must be generated within the same folder as a post (I hope I'm wrong here -- am i?), then there's no way to preview the images since there's no way to dynamically generate the path to the image.

Please tell me I'm wrong. There simply must be a way to preview images within the document editor!

Here's the relevant code (keystatic.config.tsx)

// keystatic.config.tsx

import { config, fields, collection, component } from '@keystatic/core'
import React from 'react'

export default config({
	storage: {
		kind: 'local',
	},
	collections: {
		posts: collection({
			label: 'Posts',
			slugField: 'title',
			path: 'src/content/posts/*/',
			format: { contentField: 'content' },
			schema: {
				title: fields.slug({ name: { label: 'Title' } }),
				content: fields.document({
					label: 'Content',
					formatting: true,
					links: true,
					componentBlocks: {
						youtubeVideo: component({
							label: 'YouTube Video',
							preview: ({ fields }) => (
								<div style={{ padding: '1rem', backgroundColor: '#f9f9f9' }}>
									{fields.youtubeVideoId.value ? (
										<iframe
											width='100%'
											height='315'
											src={`https://www.youtube.com/embed/${fields.youtubeVideoId.value}`}
											frameBorder='0'
											allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
											allowFullScreen
										></iframe>
									) : (
										<p>Please enter a YouTube video ID</p>
									)}
									<h3>{fields.videoTitle.value}</h3>
									<p>{fields.videoDescription.value}</p>
								</div>
							),
							schema: {
								youtubeVideoId: fields.text({ label: 'YouTube Video ID' }),
								videoTitle: fields.text({ label: 'Video Title' }),
								videoDescription: fields.text({ label: 'Video Description' }),
							},
						}),
						astroImage: component({
							label: 'Astro Image',
							preview: ({ fields }) => {
								const image = fields.imageSrc.value
								const imageUrl = image
									? `content/posts/{post.name}/content/${image.filename}`
									: ''
								return imageUrl ? (
									<img
										src={imageUrl}
										alt={fields.imageAlt.value || 'Astro Image'}
										title={fields.imageTitle.value}
										style={{ maxWidth: '100%', height: 'auto' }}
									/>
								) : (
									<p>Please select an image</p>
								)
							},
							schema: {
								imageSrc: fields.image({
									label: 'Image Source',
									directory: 'src/content/posts/*/content',
								}),
								imageTitle: fields.text({ label: 'Image Title' }),
								imageAlt: fields.text({ label: 'Alt Text' }),
							},
						}),
					},
				}),
			},
		}),
	},
})

I would appreciate any help very much, I simply must be wrong that this is not possible!

Thank you very much.

mercury7design avatar Mar 15 '25 23:03 mercury7design

I have to admit that the docs are not that clear on it, but at the end of the first paragraph of the "Content components" article ContentView is mentioned. It is a property on almost all content components. The greater problem is that you are using the deprecated document-field, which is superseeded by the markdoc-/mdx-field; those use the new content components.

Just as a suggestion, I would consider extracting the individual content component definition and placing it in a source file near the astro render component for clarity.

DontMash avatar Jul 11 '25 21:07 DontMash