Preview in data models

Hi, I need help with my data model preview.
I’m using headless CMS for the /blog, but the preview isn’t working… The Iframe loads okay, but the content is not refreshing.
ps: I’m already using the ?url=/__builder_editing__ but how can I add it automatically to all previews URL in the blogs entries?

Thanks!

Hi @emiliano what data models are you previewing? What is the tech stack of your app setup? I recommend checking this forum post How to live edit and preview from Builder Data models with React? to see an example of how we have implemented something similar

Let me know if that helps or if you have further questions!

Hi! thanks for the reply. I actually didn’t find how to do it with headless cms (data models) with lots of entries (e.g a blog). How does the <BuilderContent> component receives the model prop if there are more than 100 entries for that model? or how should I do it?
Thanks!

Hey @emiliano Could you possibly clarify your question? If the model prop is the same for all blog pages, then in the file for that blog page you can just manually hardcode the model <BuilderContent model="blog" />.

When you say headless CMS do you mean that you are pulling data from outside of Builder into Builder?

Hi! no… I think I wasn’t clear enough.
I have a data model called ‘blog model’, and I have 100 entries for that (they work as blogposts).
How could I preview those in the Builder dashboard when editing or creating them?

I’m using Next.js, so currently I fetch the blog with getStaticProps. How could I link that to the <BuilderContent model="blog-model" /> ? I see it receives some sort of data, as you explain in this post

<BuilderContent model="blog-model">{ (data) => {
          return <>
                  <h1>{data.title}</h1>
                 <p>{data.subtitle}</p>
                 </>
      }}</BuilderContent>

this doesn’t work for me.

@emiliano I see…are you building the blog page template within builder? Or only as code within your app? There are a couple ways you could do this, so depending on how yours is set up I can offer different approaches.

yes, I’m using Builder in this case only as headless CMS in my Next js app. I use getStaticProps to fetch the blog data.
Basically, I fetch the current blog post with getStaticProps and pass the data to my components (eg: <Title>{blog.data.title}</Title> )

I see, ok then yes that should be pretty straight forward!

NextJS should be able to handle all the routing, so you shouldn’t need to worry about the 100 various entries. Are you using directory and bracket notation for NextJS? Your blog page is at directory of something like app/blog/[slug].js ?

Your code would then look something like this:

builder.init(BUILDER_API_KEY)

export async function getStaticProps({params}){
  
  const blogData =
    (await builder
      .get('blog-model', {
        query: {
          'data.slug': params?.slug,
        },
      })
      .toPromise()) || null

  return {
    props: {
      blogData
    }
  }
}
...

export default function BlogPost({blogData}) {
  ...
  return (
    <BuilderContent content={blogData} model="blog-model" options={{ includeRefs: true }}>
      {(data, loading, content) => {
          <Title>{blog.data.title}</Title> 
          ...
      }
    </>
  )
}

So that you are dynamically showing and displaying the page based on the slug and you have the ability to view your preview within Builder. Just make sure that you also set the editing URL in the data model entry to be the path of where these blogs will live… Blog - YourSite.com

You can see more about adding dynamic URLs as well here: Using Advanced Editing URL Logic to set up dynamic preview URLs

I quite don’t understand it yet. I have this set up in Builder. My preview URL is ‘http://localhost:3000/blog/design-thinking-ml?url=/builder_editing’, but because I’m developing the web, but it shouldn’t be localhost:3000 at all.

In the code, I’m fetching the blog with getStaticProps and getStaticPaths (and it works perfectly). And this is my current code for the blog:

export default function Post({ blogs }) {
  return (
    <BuilderContent content={blogs} model="blog-model" options={{ includeRefs: true }}>
      {({ data, loading, content }) => (
        <div className="text-white bg-black">
          <Header
            title={`${blogs.data.title} - Scale`}
          />
          <Article post={blogs.data} content={blogs.data.content} />
          <TryBanner border />
          <Footer
            textColor="text-neutral-400"
            textHoverColor="text-white"
            borderColor="border-neutral-800"
            border
          />
        </div>
      )}
    </BuilderContent>
  );
}

the only thing missing is the preview for this, and I can’t achieve it.

@emiliano you are referencing ${blogs.data...} throughout your code, but you should just use ${data....] which is the data coming from <BuilderContent/> in this line:

As to localhost, if you have a deployed app you are looking to use instead, you should put that URL in the editing URL in your data model, and in the account page of your builder space instead of localhost:3000, per this guide: Enabling on-site previewing and editing in Builder.io - Builder.io

1 Like

@TimG Thanks!! now it’s working. My last doubt is about the new entries… to which URL should point? Because if it isn’t published yet, the preview shows a 404 page…

@emiliano actually, try adding includeUnpublished to the options on like this:

<BuilderContent content={blogs} model="blog-model" options={{ includeRefs: true, includeUnpublished: true }}>

And see if that fixes it for you

1 Like

HI @TimG , I’m reopening this question again because I found an issue. If the post has been published and unpublished later, the preview works, if it’s published the preview as well, but it it’s unpublished and has never been published at all, it throws a 404.

Here’s my code:

export default function Post({ blog }) {
  if (!blog) {
    return (
      <NoSSR>
        <Error statusCode={404} />
      </NoSSR>
    );
  }

  return (
    <BuilderContent
      content={blog}
      model="blog-model"
      options={{ includeRefs: true, includeUnpublished: true }}
    >
      {data => (
        <div className="text-white bg-black">
          <Header
            pathname={`/blog/${data?.slug}`}
            title={`${data?.title} - Scale`}
            description={data?.metaDescription}
          />
          <Article post={data} content={data?.content} />
          <TryBanner border />
          <Footer
            textColor="text-neutral-400"
          />
        </div>
      )}
    </BuilderContent>
  );
}

export async function getStaticPaths() {
  const pages = await builder.getAll('blog-model', {
    options: { noTargeting: true, includeUnpublished: true },
    omit: 'data.blocks'
  });

  return {
    paths: pages.map(page => ({ params: { slug: `${page.data.slug}` } })),
    fallback: true
  };
}

export async function getStaticProps({ params }) {
  let blog;

  try {
    const builderBlog = await builder
      .get('blog-model', {
        query: { 'data.slug': params.slug }
      })
      .promise();
    blog = builderBlog;
  } catch (error) {
    handleError(error);
  }

  if (!blog) {
    return {
      notFound: true,
      revalidate: 5
    }
  }

  return {
    props: {
      blog
      },
      revalidate: 5
    }
  };
}

Can you help me? thanks!!

@emiliano Yes, that is expected behavior as if the article is unpublished the blog object will still return null The way arround this is to add a flag to account for when you are in the Editor or the Preview views…something like this:

const isLive = !Builder.isEditing && !Builder.isPreviewing

if (!blog && isLive) {
    return (
      <NoSSR>
        <Error statusCode={404} />
      </NoSSR>
    );
  }
 ...

Try that out and see if it works for you!

1 Like