Someone an Idea if it is possible to write a LivePreviewHook for Astro (without React)?
https://payloadcms.com/docs/live-preview/frontend#building-your-own-hook
https://docs.astro.build/en/reference/integrations-reference/
somehow i don't know where to start.
import { subscribe, unsubscribe } from '@payloadcms/live-preview';
let post = {}
let SSR = true
export async function getStaticPaths() {
const data = await fetch("http://localhost:3000/api/people/");
const posts = await data.json();
return posts.docs.map((post: any) => {
return {
params: { slug: post.id },
props: { post },
};
});
}
if ( SSR ) {
const data = await fetch('http://localhost:3000/api/people/65d36f4574e7152e22a892e7?locale=fr&draft=false&depth=1');
post = await data.json()
} else {
const {post} = Astro.props;
}
---
<article>
<p set:html={post.firstName} />
</article>
SSR is just for testing a boolean variable on the top.
best regards
I haven't spent much time with either Payload or Astro, so there could be a better way but here's a quick and dirty demo I've come up with while experimenting.
Astro components just render down to standard HTML, so you'll just use dom methods to update the content on the page based on the mergedData from the subscribe callback in @payloadcms/live-preview.
Any method will depend on how the data is modeled in Payload. I have a Page collection that has a Blocks field. Each block is a component/section a content author can drop on the page (Hero, Rich Text, CTA Banner, etc).
A Page.astro component renders the page with all the blocks. A script in it subscribes to the updates from live-preview and manually updates the content based on data attributes in the markup. Here's a rough example that only handles updating text. It also doesn't handle rearranging, removing, or adding blocks.
<script>
// on Page.astro
import { subscribe, unsubscribe, ready } from '@payloadcms/live-preview';
const serverUrl = 'http://localhost:9000';
subscribe({
callback: (mergedData) => {
mergedData?.blocks?.forEach(block => {
const el = document.querySelector(`[data-id="${block.id}"]`);
if (el) {
const textFields = el.querySelectorAll('[data-text-field]');
textFields.forEach(field => {
const content = block[field.dataset.textField];
if (content !== undefined) {
field.textContent = content;
}
});
}
});
},
depth: 1,
initialData: {},
serverURL: serverUrl
});
ready({
serverURL: serverUrl
});
</script>
---
// Hero.astro
import { Picture } from 'astro:assets';
export interface Props {
data: {
__typename: String;
headline: String;
id: String;
subhead: String;
introCopy: String;
image: {
alt: String;
url: String;
}
};
}
const { data } = Astro.props;
---
<div class="container" data-component="hero" data-id={data.id}>
<div class="hero">
<Picture
alt={data.image.alt || "Missing Alt Text"}
src={`http://localhost:9000${data.image.url}`}
inferSize={true}
aspectRatio="16:9"
widths={[400, 800, 1600]}
/>
<div class="hero-content-wrap">
<div class="hero-content">
<h2 data-text-field="subhead">{data.subhead}</h2>
<h1 data-text-field="headline">{data.headline}</h1>
<p data-text-field="introCopy">{data.introCopy}</p>
</div>
</div>
</div>
</div>
Sry for the late reply.
Thank you so much. Got it working with your snippet.
Star
Discord
online
Get dedicated engineering support directly from the Payload team.