New guide: AI is in production. Is your governance?

Announcing Visual Copilot - Figma to production in half the time

Builder.io
Builder.io
Contact sales

New guide: AI is in production. Is your governance?

Announcing Visual Copilot - Figma to production in half the time

publish

Use live previews to render edits in your data model without publishing them.

Although you can fetch Builder's Data Models using the Content API directly like a typical API resource, you can also use features such as live editing, previewing, and A/B testing of your Data Models within the Builder Visual Editor, all while using standard JS/TS syntax.

To get the most out of this document, you should already be familiar with:

Comparison of a view using live preview to edit data models vs a view that does not user live preview.

Live preview is crucial when working with custom fields, data models, and structured data in Builder. It offers several benefits:

  • Real-time updates by displaying changes immediately in the Visual Editor without publishing.
  • Improved user experience to meet your expectations with an intuitive experience.
  • Iterate more quickly on your content and designs more efficiently.
  • Make sure your Sections, Pages, and structured data are exactly as intended before publishing.

Live previewing can support a smoother experience, with changes in Custom Fields, Data Models, or Structured Data updates in real-time.

  1. Specify the name of your data model in BuilderContent with the model prop.
  2. Use a render prop pattern with the required data, an optional loading, and fullData parameters in case of SSR.
  3. Add code that accesses your data in the return() statement. This code varies and depends on your use case.
  4. Set the Preview URL on the data model. For detailed instructions on setting a Preview URL on a model, visit the Setting a persistent Preview URL on a model in Editing and Previewing Your Site.
  5. Test the live preview by editing your data model in the Builder Visual Editor and checking that the changes are reflected in your application.

The following snippet shows this structure.

// Add your data model's name
<BuilderContent model="YOUR_DATA_MODEL"> 
  // add function to render data
  {(data, loading, fullData) => {
    if (loading) return <div>Loading...</div>;
    return (
      // Add your code to access your data
    );
  }}
</BuilderContent>

This example uses a custom data model named blog-article, which includes a title, author, handle, and publishedDate fields, each of type text.

The component calls builder.get() to retrieve the published entry from the blog-article model by matching the urlPath to the route parameter.

The retrieved data is passed to BlogArticle component as a prop.

import { useEffect } from "react";
import { builder, BuilderContent, useIsPreviewing } from "@builder.io/react";
import { useParams } from "react-router";
import BlogArticle from "./BlogArticle";

builder.init(/* ADD YOUR PUBLIC API KEY HERE */);

type ArticleData = {
  title: string;
  description: string;
  author: string;
};

type Article = BuilderContent & {
  data: ArticleData;
};

export default function BuilderPage() {
  const isPreviewingInBuilder = useIsPreviewing();
  const [notFound, setNotFound] = React.useState(false);
  const [content, setContent] = React.useState<Article | null>(null);
  const { slug } = useParams();

  // get the page content from Builder
  useEffect(() => {
    async function fetchContent() {
      const content = await builder
        .get("blog-article", {
          url: slug,
        })
        .promise();
      setContent(content);
      setNotFound(!content);

      if (content?.data.title) {
        document.title = content.data.title;
      }
    }
    fetchContent();
  }, [slug]);

  if (content === null) {
    return;
  }

  if (notFound && !isPreviewingInBuilder) {
    return <div>404 Page Not Found</div>;
  }

  return (
    <>
      {/* Render the Builder page */}
      <BlogContent article={content} />
    </>
  );
}

The fetched article data is passed to the <BuilderContent> component for rendering. The inline function used within <BuilderContent> accepts the following parameters:

  • data (required) – The resolved data from the blog-article model. If A/B testing is active, <BuilderContent> automatically serves the winning variant without additional setup.
  • loading (optional) – A boolean indicating whether the data is still loading.
  • fullData (required for SSR) – Includes all raw data from the Content API, such as A/B variations and metadata. In client-side rendering, <BuilderContent> fetches the most recently published entry of the specified model if the content prop is not provided.
//app/src/components/BlogArticle.tsx

import {
  BuilderComponent,
  BuilderContent,
  useIsPreviewing,
} from "@builder.io/react";

type ArticleData = {
  title: string;
  description: string;
  author: string;
};

type Article = BuilderContent & {
  data: ArticleData;
};

export default function BlogContent({ article }: { article: Article }) {
  const isPreviewing = useIsPreviewing();
  if (!isPreviewing && !article) {
    return (
      <>
        <div>404 - Page Not Found</div>
      </>
    );
  }
  return (
    <>
      <BuilderContent
        content={article}
        options={{ includeRefs: true }}
        model="blog-article"
      >
        {(data, loading, fullData) => {
          return (
            <>
              <!-- data coming from the blog-article data model -->
              <div>Blog Title: {data.title}</div>
              <div>Blog Author: {data.author}</div>
              <div>Blog handle: {data.handle}</div>
              
              {loading && <div>Loading...</div>}
             
             <!-- You can render builder components from Page/section model -->
              <BuilderComponent
                content={article}
                model="page"
                options={{ includeRefs: true }}
              />

              <div>Published Date: {data.publishedDate.Default}</div>
            </>
          );
        }}
      </BuilderContent>
    </>
  );
}

Fields such as title, author, handle, and publishedDate update in real time within the Visual Editor and do not require a publish action.

The <BuilderContent> component enables dynamic visual layouts for content entries and supports drag-and-drop editing.

This example uses a custom data model called site-settings, which includes a navigationLinks field of type list. Each link contains a linkURL and linkText sub-field.

import { BuilderContent } from "@builder.io/react";

export const Navigation = (props) => {
  return (
    <BuilderContent model="site-settings">
      {(data, loading, fullData) => {
        if (loading) {
          return <div>Loading...</div>;
        } else {
          return (
            <>
              <ul>
                {data?.navigationLinks?.map((link) => {
                  return (
                    <div key={link.linkUrl}>
                      <a target="_blank" href={link.linkUrl}>
                        {link.linkText}
                      </a>
                    </div>
                  );
                })}
              </ul>
              {props.children}
            </>
          );
        }
      }}
    </BuilderContent>
  );
};

The BuilderContent component uses the site-settings data model to fetch the most recent published entry of the provided model for rendering.

The inline function provides three parameters:

  • data (required): The resolved data from the site-settings model is the structured data from the most recent entry. If A/B testing is active, <BuilderContent> automatically serves the winning variant without additional setup.
  • loading (optional): A boolean indicating whether the data is still loading.
  • fullContent (required for SSR): Includes all raw data from the Content API, such as A/B variations and metadata. In client-side rendering, <BuilderContent> fetches the most recently published entry of the specified model if the content prop is not passed.

The video below shows using this example data model in the Visual Editor:

For more information on the variety of custom fields, visit Custom Fields.

Was this article helpful?