Vue Storefront is now Alokai! Learn More
Enabling Live Preview

Enabling Live Preview

Contentful has a reliable Live Preview feature. It allows editors to display a preview of their website within Contentful and verify their content changes as they make them. Alokai gives you a straightforward way to enable the feature in your frontend application. This guide explains how to do it.

live preview gif

Configuring Live Preview in Contentful

Enabling Live Preview starts with setting up content preview urls in Contentful. You can do it by following this guide from Contentful. In the default Alokai setup, we recommend setting content previews for two content types acting as containers for other components: Page and Layout.

live preview config

This way we are basically telling Contentful that - whenever we start editing some Page or Layout entry and open up the Live Preview window - it should display our app running on http://localhost:3000.

For Page, notice how we have assigned the {entry.fields.url} token to the ?preview query param. We've chosen the url field because that's the one we use to fetch pages in our frontend setup. We are going to use it later in our frontend app to make sure Live Preview always shows the correct page while editing.

Configuring Live Preview in your frontend

This part of the guide explains how to implement the Live Preview feature for dynamic CMS pages in your frontend application. We are going to implement the relevant logic in the script of [...slug].vue - a catch-all route component recognized by Nuxt.

Good to know

This guide uses Nuxt as an example.

Good to know

If you have followed the guide on bootstrapping Nuxt or Next, the components copied into your project should already have the Live Preview logic in them. Verify the feature works and if yes - feel free to skip the rest of this guide.

If the <script /> of your catch-all component looks like this...

<script lang="ts" setup>
import type { Entry } from 'contentful';
import { sdk } from '~/sdk.config';
import RenderComponent from '../components/cms/wrappers/RenderComponent.vue';

definePageMeta({ layout: "contentful" });

const route = useRoute();
const url = route.path;

const content: Ref<Entry<any>[] | null> = useState(() => [])
const extractedPage = computed(() => content.value?.[0])
const extractedComponents = computed(() => sdk.contentful.utils.extractComponents(extractedPage.value?.fields.items));

const { data } = await useAsyncData(() => sdk.contentful.getContent({ url }));
content.value = data.value;
</script>

...it is only fetching a dynamic page from Contentful by the value of its url field but it does not subscribe to any content changes within the Live Preview window. To change that, we need to use the initLivePreview() method provided by the Alokai's SDK module for Contentful.

It is a framework-agnostic utility which simplifies the Live Preview setup. You can call it within the onMounted() lifecycle hook or even within the setup function itself. Just make sure it runs only once, on the initial page load:

  <script lang="ts" setup>
  import type { Entry } from 'contentful';
  import { sdk } from '~/sdk.config';
  import RenderComponent from '../components/cms/wrappers/RenderComponent.vue';
  
  definePageMeta({ layout: "contentful" });
  
  const route = useRoute();
  const url = route.path;
  
  const content: Ref<Entry<any>[] | null> = useState(() => [])
  const extractedPage = computed(() => content.value?.[0])
  const extractedComponents = computed(() => sdk.contentful.utils.extractComponents(extractedPage.value?.fields.items));
  
+ sdk.contentful.utils.initLivePreview(content.value);
  
  const { data } = await useAsyncData(() => sdk.contentful.getContent({ url }));
  content.value = data.value;
  </script>

Notice how we are passing the reference to content as the first argument. The initLivePreview() method will update it whenever any changes are made in Contentful.

Read more

The initLivePreview() method can be customized. Read the documentation to find out how.

The last step required to enable Live Preview for dynamic pages is related to the {entry.fields.id} token we had configured in the previous section of this guide. While editing, Contentful will assign the value of the url field to the ?preview query param and this is where we can read it from in our catch-all component.

  <script lang="ts" setup>
  import type { Entry } from 'contentful';
  import { sdk } from '~/sdk.config';
  import RenderComponent from '../components/cms/wrappers/RenderComponent.vue';
  
  definePageMeta({ layout: "contentful" });
  
  const route = useRoute();
+ const url = route.query.preview ?? route.path;
  
  const content: Ref<Entry<any>[] | null> = useState(() => [])
  const extractedPage = computed(() => content.value?.[0])
  const extractedComponents = computed(() => sdk.contentful.utils.extractComponents(extractedPage.value?.fields.items));
  
  sdk.contentful.utils.initLivePreview(content.value);
  
  const { data } = await useAsyncData(() => sdk.contentful.getContent({ url }));
  content.value = data.value;
  </script>

This way we make sure that the Live Preview iframe will always show the page we are editing (e.g. /about) and not the default page configured as the preview url (in this case - http://localhost:3000).