Is this the right approach to previewing a drafts Structured Data + Editor

NextJS 13 with App Router, I have the following:

/src/app/blog/[...slug].js

const getArticleDetails = async (slug) => {
  const response = builder
    .get('blog-article', {
      prerender: false,
      options: { includeRefs: true },
      query: {
        'data.urlSlug': `/blog/${slug}`,
      },
    })
    .toPromise();
  return response;
};

const BlogArticleDetailPage = async (props) => {
  const { slug } = props.params;
  const fullPath = slug.join('/');
  const article = await getArticleDetails(fullPath);
  return (
    <main>
      <BlogArticleLayout
        blogArticle={article}
      />
    </main>
  );
}
/src/components/BlogArticleLayout.js
function BlogArticleLayout({ blogArticle }) {
  return (
    <BuilderContent
      model="blog-article"
      content={blogArticle}
      options={{ includeRefs: true }}>
      {(data, isLoading, fullData) => {
        return (
          <h1>{data.title}</h1>
          {/* ...other components that render off of `data` */}
          <BuilderComponent content={fullData} model="blog-article" />
        );
      }}
    </BuilderContent>
  );
}

Scenario: all of the components/elements within BuilderContent are rendering perfectly as expected. The data is updated in Preview as I update them.

However, when I add BuilderComponent to also display the Drag + Drop content, it goes into a cycle of endlessly refreshing/rehydrating.

I’m sure this must be some incorrect combination of sorts. I just can’t find the correct combo.

Additional context. If I don’t add options={{ includeRefs: true }} it also works without the endless refresh, but of course, it doesn’t update my nested refs when I make changes.

Hi @brucewyne,

Could you try to pass enrich in the options? I believe that should help with getting the updated nested refs.

 options: {
            enrich: true,
        }

Some reference code snippet:

export async function getStaticProps({ 
    params 
}: GetStaticPropsContext<{ slug: string[] }>) {
   const articleData =  
    (await builder
        .get('article',{
        options: {
            enrich: true,
        },
        query:{
            'data.slug': params?.slug
        }
     }).toPromise()) || null;

 const articleTemplate = await builder
    .get('blog', {
    }).toPromise();

  return {
    props: {
        articleData,
        articleTemplate,
    },
    revalidate: 5,
  };
}

export async function getStaticPaths() {
    const articles = await builder.getAll('article', {
      options: {noTargeting: true},
      omit: 'data.blocks'
    })
  
    articles.map(article => console.log(`/blog/${article.data?.slug}`),)
    
    return {
      paths : articles.map(article => `/blog/${article.data?.slug}`),
      fallback: true,
    };
  }

  export default function Page({
    articleTemplate ,
    articleData
   }:InferGetStaticPropsType<typeof getStaticProps>) {
 const router = useRouter();
 if (router.isFallback) {
   return <h1>Loading...</h1>;
 }
 const isLive = !Builder.isEditing && !Builder.isPreviewing;

 if (!articleData && isLive) {
   return (
     <>
       <Head>
         <meta name="robots" content="noindex" />
         <meta name="title"></meta>
       </Head>
       <DefaultErrorPage statusCode={404} />
     </>
   );
 }

 return (
    <>
     <Head>
       <meta name="viewport" content="width=device-width, initial-scale=1" />
       {!articleData && <meta name='robots' content='noindex'/>}
     </Head>
      <BuilderComponent model="blog" 
               content={articleTemplate} 
               data={{article: articleData?.data}} 
       />
   </>
 );
}

Regarding the endless refreshing when add the builderComponent , it will be helpful if you could share a recording of the issue with the steps performed?
I would like to examine this further, thank you for reaching out!

Here’s an example for reference.

Loom | Free Screen & Video Recording Software | Loom - here’s a live demo. As far as I can tell, most of what I have here follows the example.

In this video, I do pass props.blogArticle to the BuilderComponent rather than fullContent from the BuilderContent component. However I’ve tried it both ways and it doesn’t make a difference.

Hi @brucewyne !

I have a couple of questions regarding your setup:

  1. SDK Version: Could you please let me know which version of the SDK you are currently using? If possible, consider updating to the latest version.
  2. Purpose of : I’m curious about the purpose of using <BuilderContent/>. Is it primarily for enabling live editing of custom input fields?

If updating the SDK doesn’t resolve the issue you’re facing, here are a couple of alternative approaches you can consider:

Option 1 (Passing blogArticle data to content instead of fullData):

/src/components/BlogArticleLayout.js
function BlogArticleLayout({ blogArticle }) {
  return (
    <BuilderContent
      model="blog-article"
      content={blogArticle}
      options={{ includeRefs: true }}>
      {(data, isLoading, fullData) => {
        return (
          <h1>{data.title}</h1>
          {/* ...other components that render off of `data` */}
          <BuilderComponent content={blogArticle} model="blog-article" />
        );
      }}
    </BuilderContent>
  );
}

Option 2 (Moving outside of ):

/src/components/BlogArticleLayout.js
function BlogArticleLayout({ blogArticle }) {
  return (
    <BuilderContent
      model="blog-article"
      content={blogArticle}
      options={{ includeRefs: true }}>
      {(data, isLoading, fullData) => {
        return (
          <h1>{data.title}</h1>
          {/* ...other components that render off of `data` */}
        );
      }}
    </BuilderContent>
    <BuilderComponent content={blogArticle} model="blog-article" />
  );
}

I hope this helps! Let me know if you have any further questions or if there’s anything else I can assist you with.