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: Editing an Previewing Your Site - 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

Hi TimG & @nicke920 ,

I am currently in the process of migrating a Wordpress blog to Builder.

I’m currently putting all of the content of the blogs in data models, called ‘blog-data’, same as emiliano above. The difference is that we are using angular as our tech stack, and also, I am building the blog page template within builder. I personally don’t have access to our angular app, so hoping to be able to do this all inside builder.

For the home page, and build page to display each blog article, I created a blog page model is called ‘blog’. The base, or home page will be www.crexi.com - The Commercial Real Estate Exchange, and then each blog article will be www.crexi.com - The Commercial Real Estate Exchange. We use dynamic routing for this from angular.

I’d like to be able to preview each blog article like emiliano, but also make sure that each time I create a new data model of ‘blog-data’, it creates a new blog page.

My questions are:

  1. Can I have, within the same page model ‘blog’, a blog homepage template with the base url "www.crexi.com - The Commercial Real Estate Exchange’, & then a ‘www.crexi.com - The Commercial Real Estate Exchange’ URL for each article? The way our dynamic routing works, we need to keep the same base model the same for it to work.

  2. The way it is currently set up, does it work, that every time I create a new entry in the data model ’ blog-data’, will it automatically create a new page? Or will I have to create a new entry for the Page model ‘blog’ for each individual blog article? If that’s the case, is there a way to query dynamically from builder’s CMS, within the data bindings the slug, and then render the content from each data model based upon the query matching each blog articles slug?

Ultimately, trying to figure out how to be able to do all of this within builder, if there is an easy way without having to add additional code within our angular app.

Please let me know if this makes sense or if you need more clarification.

Hi @cedson ! Yes this should be possible. I recommend checking out this article: Dynamic Blog Article using Next.JS

Which I believe has what you are looking for. This specific article is written fro Next JS but the general logic is the same and can be applied to Angular . This will generate a new blog every time you create a data model entry, assuming you have the integration set in your app to fetch all of the data models when generating pages.

Within the template model you can use targeting to have an entry targeted to the base page at /blog that has a page of all blog data and then have the regular template at /blog/[slug]

Hope this helps let us know if oyu have any further questions!

Hi @TimG ,

I am trying to get unpublished articles visible and preview-able on a pages directory NextJS site. includeUnpublished works for getting it from the API however when previewing, its not updating the preview in the builder IO interface. It does update when I am updating a published article.

I tried passing includeUnpublished to BuilderContent however that did not work and also it complains about the typing with TypeScript.

Any thoughts?

What is even more crazy is it starts working when I have dev tools open! It reloads with a hydration error and then its updating properly on the preview.

Many headless CMS platforms allow configuring preview URLs by default. Look for settings related to “Preview Mode” or “Live Preview” within your CMS dashboard. There might be an option to add the ?url=/__builder_editing__ parameter automatically to all preview URLs generated for blog entries. Refer to your CMS documentation for specific instructions.
If your CMS doesn’t offer built-in configuration for preview URLs, you might be able to modify the templating code responsible for generating blog previews. This would involve adding logic to inject the ?url=/__builder_editing__ parameter dynamically whenever a preview URL is constructed.

However, modifying templates can vary depending on your CMS and templating language. It’s recommended to consult your CMS documentation or seek help from their support channels to ensure proper implementation.