Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

[Astro] LivePreview Hook

default discord avatar
s4pideamlast year
2 1

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

  • Selected Answer
    default discord avatar
    cdguglerlast year

    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>
    
    1 reply
  • default discord avatar
    s4pideam11 months ago

    Sry for the late reply.

    Thank you so much. Got it working with your snippet.

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.