Preview Editor

Burdy includes a preview editor as a part of the headless CMS solution. Preview Editor allows you to preview changes you make in the content, directly on the website, without needing to update the content. This helps out businesses make predictable and safe updates to the website content before publishing the version live.

Preview Editor

Setting it up

To set up the Burdy preview editor open up your .env file and add the following configuration to it:

PUBLIC_ENABLE_PREVIEW_EDITOR=true
PREVIEW_BASE_URL=http://localhost:3000

PUBLIC_ENABLE_PREVIEW_EDITOR enables the preview editor as a feature, whereas PREVIEW_BASE_URL is the URL of the website that will use the preview.

After these steps are completed, the preview editor should be available for use on your website!

Rewriting the URL

In many cases, you will need to use different rewrites, depending on the path configuration you use on the Sites. Burdy comes equipped with burdy-web-utils that will help you rewrite the URL. This is particularly useful when dealing with websites that have multiple languages or multiple regions.

Let's start by installing burdy-web-utils (or burdy-react-utils):

npm i @burdy-cms/web-utils // for general web projects
npm i @burdy-cms/react-utils // for react projects

We will be focusing more on React utilities as most of our user-base uses Next.js and React.

For Web Utilities: Burdy comes with the following exports: createRewrites (for rewriting pages), richtextToHtml (conversion of rich text content into HTML), subscribeToPreview (subscribes to Burdy iframe and handles data updates), updatePreview (informs the Burdy when page/data is switched).

In React utilities you still need to use createRewrites, but the subscribeToPreview/updatePreview is abstracted with usePreview hook, and richTextToHtml can be used via <RichText /> component.

After installing one of the packages you will have the ability to use createRewrites. As an example, the configuration which we are using for our structure is the following:

export const cmsRewrites = createRewrites({
  origin: `${process.env.NEXT_PUBLIC_CMS_URL}/api/content`,
  rewriteMap: [
    {
      source: '/:lang(fr|de)/:path*',
      rewrites: {
        page: '/sites/{lang}/{path}',
        footer: '/sites/{lang}/fragments/footer',
        header: '/sites/{lang}/fragments/header',
        docsMenu: '/sites/{lang}/fragments/docs-menu',
      },
    },
    {
      source: '/:path*',
      rewrites: {
        page: '/sites/en/{path}',
        footer: '/sites/en/fragments/footer',
        header: '/sites/en/fragments/header',
        docsMenu: '/sites/en/fragments/docs-menu',
      },
    },
  ],
});

This allows us to have the rewrites as follows:

  • For any URL which starts with /fr or /de, we will be fetching the header, footer and docs menu from /sites/{lang}/fragments/footer, and page from /sites/{lang}/{path}.
  • For any other URL we will be using English as the default - pages are fetched from /sites/en/{path}, and fragments are fetched from /sites/en/fragments/*

This allows us to have great flexibility over routing and can help us achieve localization and internationalization quite easily.

Pairing it up

To explain how the complete example works let's take a look at the implementation done in the Burdy template. In the page [[...slug]].tsx (in Next.js) we have the following:

export const getStaticPaths: GetStaticPaths = async (context) => {
  const { data: pages } = await axios.get[]>(`${process.env.NEXT_PUBLIC_CMS_URL}/api/sitemap`);
  return {
    paths: pages.map((page) => ({
      params: {
        slug: page.slugPath.replace(/^sites\/en\/?/i, '').split('/'),
      },
    })),
    fallback: false,
  };
};

export const getStaticProps: GetStaticProps = async (context) => {
  try {
    const path = '/' + ((context.params?.slug as string[]) || []).join('/');

    const previewData: any = context.previewData;
    const options: GetPostOptions = {};
    if (context.preview) {
      options.token = previewData?.token;
    }
    const { page: pagePath } = cmsRewrites.rewrite(path);
    const page = await getPost(pagePath, options);

    const contentTypeName = page.contentType.name;
    const template = Templates?.[contentTypeName];

    if (!template)
      return {
        notFound: true,
      };

    const additionalProps = (await template?.getTemplateProps?.(page, path, options)) || {};

    return {
      props: {
        page,
        ...(additionalProps || {}),
      },
    };
  } catch (e) {
    return {
      notFound: true,
    };
  }
};

This gives us the power to use Burdy as a static page generator. We've added in the getStaticPaths as well (via custom sitemap method that fetches all published pages. The endpoint needed for page content is being resolved via cmsRewrites (example provided above), and each template takes care of its own data afterward.

The render method is a collection of template pages that are grouped based on their content-type:

export const Templates = {
  'docs-page': DocsTemplate,
  'docs-post': DocsTemplate,
  'blog-page': BlogsTemplate,
  'blog-post': BlogTemplate,
  'comparison-page': ComparisonTemplate,
  'landing-page': LandingTemplate,
};

const Website: NextPage = (props) => {
  const contentTypeName = props.page.contentType.name;
  const Template = Templates?.[contentTypeName].default;

  // render TSX
};

// See getStaticProps and getStaticPaths above

export default Website;

We've also abstracted the getTemplateProps, in order to make things more Next.js friendly. See the blog post example below:

export const getTemplateProps: GetTemplateProps = async (page, path, options) => {
  const { header, footer } = cmsRewrites.rewrite(path);

  const [headerData, footerData] = await Promise.all([getPost(header, options), getPost(footer, options)]);

  const headerProps = headerData.meta.content;
  const footerProps = footerData.meta.content;

  return {
    headerProps,
    footerProps,
  };
};

The last step needed to provide Burdy with functionality of injecting the preview props into your components is usePreview hook:

import { usePreview } from '@burdy-cms/react-utils'

const BlogPage: BurdyPage = (props) => {
  const page = usePreview(props.page);
  // ... do rendering
}

export default BlogPage;

The Result

The result of this configuration is easy to manage a website that can adapt to based on the content type and provide full preview editor capability. The bare-metal version of preview is simply using the usePreview hook directly on the page. However, using the following set-up and basing the Next.js on templates will make things a lot easier and new pages will be connectable out of the box.

Copyright © Burdy Technologies. All rights reserved.