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 similar to Legacy CMS, directly on the website, without needing to update the content while keeping everything Headless under the hood. This helps out businesses make predictable and safe updates to the website content before publishing the version live.

Preview Editor

Setting it up

For example, all your content for the Website is under Folder named sites in the Burdy Sites and you have multiple languages. Your structure looks something like this:

Now create rewrite rules so the preview editor will know how post slugPaths map to the Website pages.

To set up the Burdy preview editor go to the Settings > Preview Editor tab and tick Enable preview Editor.

In the Rewrites field create JSON configuration:

[
  {
    "source": "sites/en",
    "destination": "https://website.com"
  },
  {
    "source": "sites/en/:path*",
    "destination": "https://website.com/{path}"
  },
  {
    "source": "sites/:lang(fr|de)",
    "destination": "https://website.com/{lang}"
  },
  {
    "source": "sites/:lang(fr|de)/:path*",
    "destination": "https://website.com/{lang}/{path}"
  }
]

This example will map:

sites/en > https://website.com
sites/en/about > https://website.com/about
sites/fr > https://website.com/fr
sites/fr/about > https://website.com/fr/about

Next.js example

We can always leverage NextJS api/preview capability and live preview even in production with Static Generated Pages.

Rewrite rules:

[
  {
    "source": "sites/en",
    "destination": "http://localhost:3000/api/preview?slugPath=/&secret=SomeSecret"
  },
  {
    "source": "sites/en/:path*",
    "destination": "http://localhost:3000/api/preview?slugPath=/{path}&secret=SomeSecret"
  },
  {
    "source": "sites/:lang(fr|de)",
    "destination": "http://localhost:3000/api/preview?slugPath=/{lang}&secret=SomeSecret"
  },
  {
    "source": "sites/:lang(fr|de)/:path*",
    "destination": "http://localhost:3000/api/preview?slugPath=/{lang}/{path}&secret=SomeSecret"
  }
]

Burdy can now successfully rewrite slugPath from Burdy Sites to your Website Pages!

Reverse Rewrite from the Website

As we are able to map from Burdy to Website now it is time for the Website to understand how to map Pages to Burdy Sites so it will be able to pull Hierarchical content. This is particularly useful when dealing with websites that have multiple languages or multiple regions.

Our goal is now to create rewrite rules to get:

https://website.com > sites/en
https://websites.com/about > sites/en/about
https://websites.com/fr > sites/fr
https://websites.com/fr/about > sites/fr/about

To achieve this in your Website (React, Next, Angular...) first 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.

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 burdyMapRewrites = createRewrites<{
  page: string,
  footer: string,
  header: string,
  docsMenu: string
}>({
  rewrite: [
    {
      source: '/:lang(fr|de)',
      destination: {
        page: 'sites/{lang}',
        footer: 'sites/{lang}/fragments/footer',
        header: 'sites/{lang}/fragments/header',
        docsMenu: 'sites/{lang}/fragments/docs-menu',
      },
    },
    {
      source: '/:lang(fr|de)/:path*',
      destination: {
        page: 'sites/{lang}/{path}',
        footer: 'sites/{lang}/fragments/footer',
        header: 'sites/{lang}/fragments/header',
        docsMenu: 'sites/{lang}/fragments/docs-menu',
      },
    },
    {
      source: '/',
      destination: {
        page: 'sites/en',
        footer: 'sites/en/fragments/footer',
        header: 'sites/en/fragments/header',
        docsMenu: 'sites/en/fragments/docs-menu',
      },
    },
    {
      source: '/:path*',
      destination: {
        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 Next.js page we have the following:

export const getStaticProps: GetStaticProps = async (context) => {
  try {
    const path = `/${((context?.params?.slug as string[]) || []).join('/')}`;
    const previewData: any = context.previewData;
    const { page: pagePath } = burdyMapRewrites.rewrite(path);

    const options: any = {
      draft: !!previewData,
    };

    let page;
    try {
      page = await burdyApi.getPage(pagePath as string, options);
    } catch (err) {
      return {
        notFound: true,
      };
    }

    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 capability to use Burdy as a static page generator!

Let's expand and create a full dynamic Next.js website with just one page, [[...slug]].tsx.

GetStaticPaths:

export const getStaticPaths: GetStaticPaths = async (context) => {
  const { results: pages } = await burdyApi.searchPages({
    parent: 'sites',
  });

  return {
    paths: (pages || []).map((page) => {
      const pagePath = pathRewrites.rewrite(page.slugPath);
      return {
        params: {
          slug: pagePath?.split('/'),
        },
      };
    }),
    fallback: false,
  };
};

And its own rewrite rules (similar to Burdy Preview Editor rewrite rules) which rewrite slugPaths to pages:

export const pathRewrites: any = createRewrites<string>({
  rewrite: [
    {
      source: 'sites/:lang(fr|de)',
      destination: '{lang}'
    },
    {
      source: 'sites/:lang(fr|de)/:path*',
      destination: '{lang}/{path}'
    },
    {
      source: 'sites/en/:path*',
      destination: '{path}'
    },
    {
      source: 'sites/en',
      destination: ''
    }
  ],
});

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:

import * as DocsTemplate from './templates/docs.page';
import * as BlogsTemplate from './templates/blogs.page';
import * as BlogTemplate from './templates/blog.page';
import * as LandingTemplate from './templates/landing.page';

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


const Website: NextPage<WebsiteProps> = (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 from each template allowing templates to pull additional content if needed, 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<BlogsTemplateProps> = (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.